import { assert , log , LogLevel } from './auxiliaries' ; import { Context } from './context' ; import { Framebuffer } from './framebuffer' ; import { GaussFilter } from './gaussfilter' ; import { Initializable } from './initializable' ; import { Renderbuffer } from './renderbuffer' ; import { Texture2D } from './texture2d' ; import { Wizard } from './wizard' ; import { GLsizei2 } from './tuples' ; export class ShadowPass extends Initializable { protected _context : Context ; protected _shadowType : ShadowPass . ShadowMappingType ; protected _shadowMapSize : GLsizei2 ; protected _blurredShadowMapSize : GLsizei2 ; protected _shadowMapFBO : Framebuffer ; protected _shadowMapTexture : Texture2D ; protected _shadowMapRenderbuffer : Renderbuffer ; protected _gaussFilter : GaussFilter ; protected _gaussFilterKernelSize : GLsizei = 21 ; protected _intermediateBlurFBO : Framebuffer ; protected _intermediateBlurTexture : Texture2D ; protected _blurFBO : Framebuffer ; protected _blurTexture : Texture2D ; constructor ( context ) { super ( ) ; this . _context = context ; } get shadowMapFBO ( ) : Framebuffer { return this . _shadowMapFBO ; } get shadowMapTexture ( ) : Texture2D { if ( this . hasBlur ) { return this . _blurTexture ; } return this . _shadowMapTexture ; } get hasBlur ( ) : boolean { return this . _shadowType !== ShadowPass . ShadowMappingType . HardLinear ; } get blurSize ( ) : GLsizei { return this . _gaussFilterKernelSize ; } set blurSize ( blurSize ) { if ( blurSize === this . _gaussFilterKernelSize ) { return ; } if ( this . _gaussFilter !== undefined ) { this . _gaussFilter . kernelSize = blurSize ; this . _gaussFilter . standardDeviation = blurSize / 6.0 ; } this . _gaussFilterKernelSize = blurSize ; } @ Initializable . assert_initialized ( ) resize ( size , bind = true , unbind = true ) { assert ( size [ 0 ] > 0 && size [ 1 ] > 0 , 'Size has to be > 0.' ) ; this . _shadowMapSize = size ; this . _shadowMapFBO . resize ( this . _shadowMapSize [ 0 ] , this . _shadowMapSize [ 1 ] , bind , unbind ) ; } @ Initializable . assert_initialized ( ) resizeBlurTexture ( size , bind = true , unbind = true ) { assert ( size [ 0 ] > 0 && size [ 1 ] > 0 , 'Size has to be > 0.' ) ; this . _blurredShadowMapSize = size ; this . _intermediateBlurFBO . resize ( this . _blurredShadowMapSize [ 0 ] , this . _blurredShadowMapSize [ 1 ] , bind , unbind ) ; this . _blurFBO . resize ( this . _blurredShadowMapSize [ 0 ] , this . _blurredShadowMapSize [ 1 ] , bind , unbind ) ; } @ Initializable . initialize ( ) initialize ( shadowType , shadowMapSize , blurredShadowMapSize ? ) { assert ( shadowMapSize [ 0 ] > 0 && shadowMapSize [ 1 ] > 0 , 'Size has to be > 0.' ) ; this . _shadowType = shadowType ; this . _shadowMapSize = shadowMapSize ; if ( blurredShadowMapSize !== undefined ) { this . _blurredShadowMapSize = blurredShadowMapSize ; } else { this . _blurredShadowMapSize = this . _shadowMapSize ; } const gl = this . _context . gl as WebGLRenderingContext ; const gl2facade = this . _context . gl2facade ; let format = gl . RGBA ; if ( this . _context . isWebGL2 ) { const gl2 = this . _context . gl as WebGL2RenderingContext ; switch ( this . _shadowType ) { case ShadowPass . ShadowMappingType . HardLinear : case ShadowPass . ShadowMappingType . HardExponential : format = gl2 . RED ; break ; case ShadowPass . ShadowMappingType . SoftLinear : format = gl2 . RG ; break ; case ShadowPass . ShadowMappingType . SoftExponential : format = gl2 . RGBA ; break ; default : assert ( false , 'Unexpected value for shadowType' ) ; } } const [ internalFormat , type ] = Wizard . queryInternalTextureFormat ( this . _context , format , Wizard . Precision . float ) ; if ( this . _shadowType !== ShadowPass . ShadowMappingType . HardLinear && type !== gl . FLOAT ) { log ( LogLevel . Warning , 'floating point textures are not supported, falling back to HardLinear' ) ; this . _shadowType = ShadowPass . ShadowMappingType . HardLinear ; } let filter = gl . LINEAR ; if ( type === gl . FLOAT && ! this . _context . supportsTextureFloatLinear ) { filter = gl . NEAREST ; } if ( type === gl2facade . HALF_FLOAT && ! this . _context . supportsTextureHalfFloatLinear ) { filter = gl . NEAREST ; } this . _shadowMapTexture = new Texture2D ( this . _context ) ; this . _shadowMapTexture . initialize ( this . _shadowMapSize [ 0 ] , this . _shadowMapSize [ 1 ] , internalFormat , format , gl . FLOAT ) ; this . _shadowMapTexture . wrap ( gl . CLAMP_TO_EDGE , gl . CLAMP_TO_EDGE ) ; this . _shadowMapTexture . filter ( filter , filter ) ; this . _shadowMapRenderbuffer = new Renderbuffer ( this . _context ) ; this . _shadowMapRenderbuffer . initialize ( this . _shadowMapSize [ 0 ] , this . _shadowMapSize [ 1 ] , gl . DEPTH_COMPONENT16 ) ; this . _shadowMapFBO = new Framebuffer ( this . _context ) ; this . _shadowMapFBO . initialize ( [ [ gl2facade . COLOR_ATTACHMENT0 , this . _shadowMapTexture ] , [ gl . DEPTH_ATTACHMENT , this . _shadowMapRenderbuffer ] ] ) ; this . _shadowMapFBO . clearColor ( [ 1.0 , 1.0 , 1.0 , 1.0 ] ) ; this . _shadowMapFBO . clearDepth ( 1.0 ) ; if ( this . hasBlur ) { this . _gaussFilter = new GaussFilter ( this . _context ) ; this . _gaussFilter . kernelSize = this . _gaussFilterKernelSize ; this . _gaussFilter . standardDeviation = this . _gaussFilterKernelSize / 6.0 ; this . _gaussFilter . initialize ( ) ; this . _intermediateBlurTexture = new Texture2D ( this . _context , 'IntermediateBlurTexture' ) ; this . _intermediateBlurTexture . initialize ( this . _blurredShadowMapSize [ 0 ] , this . _blurredShadowMapSize [ 1 ] , internalFormat , format , gl . FLOAT ) ; this . _intermediateBlurTexture . wrap ( gl . CLAMP_TO_EDGE , gl . CLAMP_TO_EDGE ) ; this . _intermediateBlurTexture . filter ( filter , filter ) ; this . _intermediateBlurFBO = new Framebuffer ( this . _context , 'IntermediateBlurFramebuffer' ) ; this . _intermediateBlurFBO . initialize ( [ [ gl2facade . COLOR_ATTACHMENT0 , this . _intermediateBlurTexture ] ] ) ; this . _intermediateBlurFBO . clearColor ( [ 1.0 , 1.0 , 1.0 , 1.0 ] ) ; this . _intermediateBlurFBO . clearDepth ( 1.0 ) ; this . _blurTexture = new Texture2D ( this . _context , 'BlurTexture' ) ; this . _blurTexture . initialize ( this . _blurredShadowMapSize [ 0 ] , this . _blurredShadowMapSize [ 1 ] , internalFormat , format , gl . FLOAT ) ; this . _blurTexture . wrap ( gl . CLAMP_TO_EDGE , gl . CLAMP_TO_EDGE ) ; this . _blurTexture . filter ( filter , filter ) ; this . _blurFBO = new Framebuffer ( this . _context , 'BlurFramebuffer' ) ; this . _blurFBO . initialize ( [ [ gl2facade . COLOR_ATTACHMENT0 , this . _blurTexture ] ] ) ; this . _blurFBO . clearColor ( [ 1.0 , 1.0 , 1.0 , 1.0 ] ) ; this . _blurFBO . clearDepth ( 1.0 ) ; } return true ; } @ Initializable . uninitialize ( ) uninitialize ( ) { this . _shadowMapFBO . uninitialize ( ) ; this . _shadowMapRenderbuffer . uninitialize ( ) ; this . _shadowMapTexture . uninitialize ( ) ; if ( this . hasBlur ) { this . _intermediateBlurFBO . uninitialize ( ) ; this . _intermediateBlurTexture . uninitialize ( ) ; this . _blurFBO . uninitialize ( ) ; this . _blurTexture . uninitialize ( ) ; this . _gaussFilter . uninitialize ( ) ; } } @ Initializable . assert_initialized ( ) frame ( callback ) { const gl = this . _context . gl ; gl . viewport ( 0 , 0 , this . _shadowMapSize [ 0 ] , this . _shadowMapSize [ 1 ] ) ; gl . enable ( gl . DEPTH_TEST ) ; gl . depthFunc ( gl . LEQUAL ) ; this . _shadowMapFBO . clear ( gl . COLOR_BUFFER_BIT | gl . DEPTH_BUFFER_BIT , true , false ) ; callback ( ) ; gl . disable ( gl . DEPTH_TEST ) ; gl . depthFunc ( gl . LESS ) ; if ( this . hasBlur ) { gl . viewport ( 0 , 0 , this . _intermediateBlurFBO . width , this . _intermediateBlurFBO . height ) ; this . _intermediateBlurFBO . clear ( gl . COLOR_BUFFER_BIT | gl . DEPTH_BUFFER_BIT , true , false ) ; this . _gaussFilter . filter ( this . _shadowMapTexture , GaussFilter . Direction . Horizontal ) ; gl . viewport ( 0 , 0 , this . _blurFBO . width , this . _blurFBO . height ) ; this . _blurFBO . clear ( gl . COLOR_BUFFER_BIT | gl . DEPTH_BUFFER_BIT , true , false ) ; this . _gaussFilter . filter ( this . _intermediateBlurTexture , GaussFilter . Direction . Vertical ) ; } } } export namespace ShadowPass { export enum ShadowMappingType { HardLinear = 0 , SoftLinear = 1 , HardExponential = 2 , SoftExponential = 3 } }