Directional Shadow

ShaderLib

When WebGLRenderer creating material in initMaterial(), the programChange == true branch will load the shader of ShaderLib[ parameters.shaderID ].

function initMaterial( material, fog, object ) {
    if ( programChange )
        if ( parameters.shaderID )
            var shader = ShaderLib[ parameters.shaderID ];
    ...
}

In ShaderLib.js, the lib is defined as

var ShaderLib = {
    ...

    phong: {
        uniforms: mergeUniforms( [
            UniformsLib.specularmap,
            ...
            UniformsLib.lights,
            {   emissive: { value: new Color( 0x000000 ) },
                specular: { value: new Color( 0x111111 ) },
                shininess: { value: 30 }
            }
        ] ),

        vertexShader: ShaderChunk.meshphong_vert,
        fragmentShader: ShaderChunk.meshphong_frag
    },
    ...

    shadow: {
        uniforms: mergeUniforms( [
            UniformsLib.lights,
            UniformsLib.fog,
            {   color: { value: new Color( 0x00000 ) },
                opacity: { value: 1.0 }
            },
        ] ),

        vertexShader: ShaderChunk.shadow_vert,
        fragmentShader: ShaderChunk.shadow_frag
    }
};

In UniformsLib.js

var UniformsLib = {
    common: {
        diffuse: { value: new Color( 0xeeeeee ) },
        ...
    },

    specularmap: { specularMap: { value: null }, },

    lights: {
        ambientLightColor: { value: [] },
        lightProbe: { value: [] },
        directionalLights: { value: [], properties: {
            direction: {},
            color: {},

            shadow: {},
            shadowBias: {},
            shadowRadius: {},
            shadowMapSize: {}
        } },

        directionalShadowMap: { value: [] },
        directionalShadowMatrix: { value: [] },

        spotLights: { ... },
        pointLights: { ... },

        ...

        hemisphereLights: { value: [], properties: {
            direction: {},
            skyColor: {},
            groundColor: {}
        } },
    },
};

ShaderChunk is actually a boilerplate, e.g. phong vertex shader:

#define PHONG
varying vec3 vViewPosition;

#ifndef FLAT_SHADED
    varying vec3 vNormal;
#endif

#include <common>
#include <uv_pars_vertex>
#include <uv2_pars_vertex>
#include <displacementmap_pars_vertex>
#include <envmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>

void main() {

    #include <uv_vertex>
    #include <uv2_vertex>
    #include <color_vertex>

    #include <beginnormal_vertex>
    #include <morphnormal_vertex>
    #include <skinbase_vertex>
    #include <skinnormal_vertex>
    #include <defaultnormal_vertex>

#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED
    vNormal = normalize( transformedNormal );
#endif

    #include <begin_vertex>
    #include <morphtarget_vertex>
    #include <skinning_vertex>
    #include <displacementmap_vertex>
    #include <project_vertex>
    #include <logdepthbuf_vertex>
    #include <clipping_planes_vertex>

    vViewPosition = - mvPosition.xyz;

    #include <worldpos_vertex>
    #include <envmap_vertex>
    #include <shadowmap_vertex>
    #include <fog_vertex>
}

Directional Lights Uniforms

When WebGLRenderer is rendering, it maintance it’s current state, an instance of WebGLRenderState.

WebGLRenderer.render()
->  WebGLRenderState.setupLights( camera )
    ->  WebGLLights.setup( lights, shadows, camera )

The setup() function manage directional lights uniforms.

uniforms = {
    direction: new Vector3(),
    color: new Color(),

    shadow: false,
    shadowBias: 0,
    shadowRadius: 1,
    shadowMapSize: new Vector2()
};

if ( light.isDirectionalLight ) {
    var uniforms = cache.get( light );

    uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
    uniforms.direction.setFromMatrixPosition( light.matrixWorld );
    vector3.setFromMatrixPosition( light.target.matrixWorld );
    uniforms.direction.sub( vector3 );
    uniforms.direction.transformDirection( viewMatrix );

    uniforms.shadow = light.castShadow;

    if ( light.castShadow ) {

        var shadow = light.shadow;

        uniforms.shadowBias = shadow.bias;
        uniforms.shadowRadius = shadow.radius;
        uniforms.shadowMapSize = shadow.mapSize;

        state.directionalShadowMap[ directionalLength ] = shadowMap;
        state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;

        numDirectionalShadows ++;

    }

    state.directional[ array_length - 1 ] = uniforms;
}

In WebGLRenderer.initMaterial(), the lights’ state also updated into materials’ uniforms:

if ( materialProperties.needsLights ) {
    // wire up the material to this renderer's lighting state
    uniforms.ambientLightColor.value = lights.state.ambient;
    uniforms.lightProbe.value = lights.state.probe;
    uniforms.directionalLights.value = lights.state.directional;
    uniforms.spotLights.value = lights.state.spot;
    uniforms.rectAreaLights.value = lights.state.rectArea;
    uniforms.pointLights.value = lights.state.point;
    uniforms.hemisphereLights.value = lights.state.hemi;

    uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
    uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
    uniforms.spotShadowMap.value = lights.state.spotShadowMap;
    uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
    uniforms.pointShadowMap.value = lights.state.pointShadowMap;
    uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
}

This section also shows that if a material receive directional lights, it must has all the fields. In x-visual, this is been handled by thrender.createXShaderMaterial().