Chris 2pha Brown

Chris Brown

Drupal, Javascript, Three.js, 3D

website blog

Three.js standard materials vertex and fragment shaders reference

When playing around and experimenting with Three.js ShaderMaterial I often want to take a look at how the standard Three.js materials are done. Looking through the code or constantly outputting the shaders in the javascript console is a pain in the arse. I though I would post them here for future reference and in case anyone else was interested.

NOTE: These were output from Three.js version 70, three.js materials have changed a lot since then.

MeshBasicMaterial

Vertex Shader

#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    varying vec2 vUv;
    uniform vec4 offsetRepeat;

#endif

#ifdef USE_LIGHTMAP

    varying vec2 vUv2;

#endif
#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )

    varying vec3 vReflect;

    uniform float refractionRatio;

#endif

#ifdef USE_COLOR

    varying vec3 vColor;

#endif
#ifdef USE_MORPHTARGETS

    #ifndef USE_MORPHNORMALS

    uniform float morphTargetInfluences[ 8 ];

    #else

    uniform float morphTargetInfluences[ 4 ];

    #endif

#endif
#ifdef USE_SKINNING

    uniform mat4 bindMatrix;
    uniform mat4 bindMatrixInverse;

    #ifdef BONE_TEXTURE

        uniform sampler2D boneTexture;
        uniform int boneTextureWidth;
        uniform int boneTextureHeight;

        mat4 getBoneMatrix( const in float i ) {

            float j = i * 4.0;
            float x = mod( j, float( boneTextureWidth ) );
            float y = floor( j / float( boneTextureWidth ) );

            float dx = 1.0 / float( boneTextureWidth );
            float dy = 1.0 / float( boneTextureHeight );

            y = dy * ( y + 0.5 );

            vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );
            vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );
            vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );
            vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );

            mat4 bone = mat4( v1, v2, v3, v4 );

            return bone;

        }

    #else

        uniform mat4 boneGlobalMatrices[ MAX_BONES ];

        mat4 getBoneMatrix( const in float i ) {

            mat4 bone = boneGlobalMatrices[ int(i) ];
            return bone;

        }

    #endif

#endif

#ifdef USE_SHADOWMAP

    varying vec4 vShadowCoord[ MAX_SHADOWS ];
    uniform mat4 shadowMatrix[ MAX_SHADOWS ];

#endif
#ifdef USE_LOGDEPTHBUF

    #ifdef USE_LOGDEPTHBUF_EXT

        varying float vFragDepth;

    #endif

    uniform float logDepthBufFC;

#endif
void main() {
#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    vUv = uv * offsetRepeat.zw + offsetRepeat.xy;

#endif
#ifdef USE_LIGHTMAP

    vUv2 = uv2;

#endif
#ifdef USE_COLOR

    #ifdef GAMMA_INPUT

        vColor = color * color;

    #else

        vColor = color;

    #endif

#endif
#ifdef USE_SKINNING

    mat4 boneMatX = getBoneMatrix( skinIndex.x );
    mat4 boneMatY = getBoneMatrix( skinIndex.y );
    mat4 boneMatZ = getBoneMatrix( skinIndex.z );
    mat4 boneMatW = getBoneMatrix( skinIndex.w );

#endif
    #ifdef USE_ENVMAP
#ifdef USE_MORPHNORMALS

    vec3 morphedNormal = vec3( 0.0 );

    morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];
    morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];
    morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];
    morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];

    morphedNormal += normal;

#endif
#ifdef USE_SKINNING

    mat4 skinMatrix = mat4( 0.0 );
    skinMatrix += skinWeight.x * boneMatX;
    skinMatrix += skinWeight.y * boneMatY;
    skinMatrix += skinWeight.z * boneMatZ;
    skinMatrix += skinWeight.w * boneMatW;
    skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;

    #ifdef USE_MORPHNORMALS

    vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );

    #else

    vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );

    #endif

#endif

#ifdef USE_SKINNING

    vec3 objectNormal = skinnedNormal.xyz;

#elif defined( USE_MORPHNORMALS )

    vec3 objectNormal = morphedNormal;

#else

    vec3 objectNormal = normal;

#endif

#ifdef FLIP_SIDED

    objectNormal = -objectNormal;

#endif

vec3 transformedNormal = normalMatrix * objectNormal;

    #endif
#ifdef USE_MORPHTARGETS

    vec3 morphed = vec3( 0.0 );
    morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];
    morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];
    morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];
    morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];

    #ifndef USE_MORPHNORMALS

    morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];
    morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];
    morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];
    morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];

    #endif

    morphed += position;

#endif
#ifdef USE_SKINNING

    #ifdef USE_MORPHTARGETS

    vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );

    #else

    vec4 skinVertex = bindMatrix * vec4( position, 1.0 );

    #endif

    vec4 skinned = vec4( 0.0 );
    skinned += boneMatX * skinVertex * skinWeight.x;
    skinned += boneMatY * skinVertex * skinWeight.y;
    skinned += boneMatZ * skinVertex * skinWeight.z;
    skinned += boneMatW * skinVertex * skinWeight.w;
    skinned  = bindMatrixInverse * skinned;

#endif

#ifdef USE_SKINNING

    vec4 mvPosition = modelViewMatrix * skinned;

#elif defined( USE_MORPHTARGETS )

    vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 );

#else

    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

#endif

gl_Position = projectionMatrix * mvPosition;

#ifdef USE_LOGDEPTHBUF

    gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;

    #ifdef USE_LOGDEPTHBUF_EXT

        vFragDepth = 1.0 + gl_Position.w;

#else

        gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;

    #endif

#endif
#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )

    #ifdef USE_SKINNING

        vec4 worldPosition = modelMatrix * skinned;

    #elif defined( USE_MORPHTARGETS )

        vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );

    #else

        vec4 worldPosition = modelMatrix * vec4( position, 1.0 );

    #endif

#endif

#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )

    vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;
    worldNormal = normalize( worldNormal );

    vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );

    #ifdef ENVMAP_MODE_REFLECTION

        vReflect = reflect( cameraToVertex, worldNormal );

    #else

        vReflect = refract( cameraToVertex, worldNormal, refractionRatio );

    #endif

#endif

#ifdef USE_SHADOWMAP

    for( int i = 0; i < MAX_SHADOWS; i ++ ) {

        vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;

    }

#endif
}

Fragment Shader

uniform float opacity;
#ifdef USE_COLOR

    varying vec3 vColor;

#endif

#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    varying vec2 vUv;

#endif

#ifdef USE_MAP

    uniform sampler2D map;

#endif
#ifdef USE_ALPHAMAP

    uniform sampler2D alphaMap;

#endif

#ifdef USE_LIGHTMAP

    varying vec2 vUv2;
    uniform sampler2D lightMap;

#endif
#ifdef USE_ENVMAP

    uniform float reflectivity;
    #ifdef ENVMAP_TYPE_CUBE
        uniform samplerCube envMap;
    #else
        uniform sampler2D envMap;
    #endif
    uniform float flipEnvMap;

    #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )

        uniform float refractionRatio;

    #else

        varying vec3 vReflect;

    #endif

#endif

#ifdef USE_FOG

    uniform vec3 fogColor;

    #ifdef FOG_EXP2

        uniform float fogDensity;

    #else

        uniform float fogNear;
        uniform float fogFar;
    #endif

#endif
#ifdef USE_SHADOWMAP

    uniform sampler2D shadowMap[ MAX_SHADOWS ];
    uniform vec2 shadowMapSize[ MAX_SHADOWS ];

    uniform float shadowDarkness[ MAX_SHADOWS ];
    uniform float shadowBias[ MAX_SHADOWS ];

    varying vec4 vShadowCoord[ MAX_SHADOWS ];

    float unpackDepth( const in vec4 rgba_depth ) {

        const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );
        float depth = dot( rgba_depth, bit_shift );
        return depth;

    }

#endif
#ifdef USE_SPECULARMAP

    uniform sampler2D specularMap;

#endif
#ifdef USE_LOGDEPTHBUF

    uniform float logDepthBufFC;

    #ifdef USE_LOGDEPTHBUF_EXT

        #extension GL_EXT_frag_depth : enable
        varying float vFragDepth;

    #endif

#endif
void main() {
    gl_FragColor = vec4( diffuse, opacity );
#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)

    gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;

#endif
#ifdef USE_MAP

    vec4 texelColor = texture2D( map, vUv );

    #ifdef GAMMA_INPUT

        texelColor.xyz *= texelColor.xyz;

    #endif

    gl_FragColor = gl_FragColor * texelColor;

#endif
#ifdef USE_ALPHAMAP

    gl_FragColor.a *= texture2D( alphaMap, vUv ).g;

#endif

#ifdef ALPHATEST

    if ( gl_FragColor.a < ALPHATEST ) discard;

#endif

float specularStrength;

#ifdef USE_SPECULARMAP

    vec4 texelSpecular = texture2D( specularMap, vUv );
    specularStrength = texelSpecular.r;

#else

    specularStrength = 1.0;

#endif
#ifdef USE_LIGHTMAP

    gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );

#endif
#ifdef USE_COLOR

    gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );

#endif
#ifdef USE_ENVMAP

    #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )

        vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );

        // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transforma...        // Transforming Normal Vectors with the Inverse Transformation

        vec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );

        #ifdef ENVMAP_MODE_REFLECTION

            vec3 reflectVec = reflect( cameraToVertex, worldNormal );

        #else

            vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );

        #endif

    #else

        vec3 reflectVec = vReflect;

    #endif

    #ifdef DOUBLE_SIDED
        float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );
    #else
        float flipNormal = 1.0;
    #endif

    #ifdef ENVMAP_TYPE_CUBE
        vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );

    #elif defined( ENVMAP_TYPE_EQUIREC )
        vec2 sampleUV;
        sampleUV.y = clamp( flipNormal * reflectVec.y * 0.5 + 0.5, 0.0, 1.0);
        sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * 0.15915494309189533576888376337251 + 0.5; // reciprocal( 2 PI ) + 0.5
        vec4 envColor = texture2D( envMap, sampleUV );

    #elif defined( ENVMAP_TYPE_SPHERE )
        vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));
        vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );
    #endif

    #ifdef GAMMA_INPUT

        envColor.xyz *= envColor.xyz;

    #endif

    #ifdef ENVMAP_BLENDING_MULTIPLY

        gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * envColor.xyz, specularStrength * reflectivity );

    #elif defined( ENVMAP_BLENDING_MIX )

        gl_FragColor.xyz = mix( gl_FragColor.xyz, envColor.xyz, specularStrength * reflectivity );

    #elif defined( ENVMAP_BLENDING_ADD )

        gl_FragColor.xyz += envColor.xyz * specularStrength * reflectivity;

    #endif

#endif

#ifdef USE_SHADOWMAP

    #ifdef SHADOWMAP_DEBUG

        vec3 frustumColors[3];
        frustumColors[0] = vec3( 1.0, 0.5, 0.0 );
        frustumColors[1] = vec3( 0.0, 1.0, 0.8 );
        frustumColors[2] = vec3( 0.0, 0.5, 1.0 );

    #endif

    #ifdef SHADOWMAP_CASCADE

        int inFrustumCount = 0;

    #endif

    float fDepth;
    vec3 shadowColor = vec3( 1.0 );

    for( int i = 0; i < MAX_SHADOWS; i ++ ) {

        vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;

                // if ( something && something ) breaks ATI OpenGL shader compiler
                // if ( all( something, something ) ) using this instead

        bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );
        bool inFrustum = all( inFrustumVec );

                // don't shadow pixels outside of light frustum
                // use just first frustum (for cascades)
                // don't shadow pixels behind far plane of light frustum

        #ifdef SHADOWMAP_CASCADE

            inFrustumCount += int( inFrustum );
            bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );

        #else

            bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );

        #endif

        bool frustumTest = all( frustumTestVec );

        if ( frustumTest ) {

            shadowCoord.z += shadowBias[ i ];

            #if defined( SHADOWMAP_TYPE_PCF )

                        // Percentage-close filtering
                        // (9 pixel kernel)
                        // http://fabiensanglard.net/shadowmappingPCF/
                float shadow = 0.0;

        /*
                        // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
                        // must enroll loop manually

                for ( float y = -1.25; y <= 1.25; y += 1.25 )
                    for ( float x = -1.25; x <= 1.25; x += 1.25 ) {

                        vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );

                                // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup
                                //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );

                        float fDepth = unpackDepth( rgbaDepth );

                        if ( fDepth < shadowCoord.z )
                            shadow += 1.0;

                }

                shadow /= 9.0;

        */

                const float shadowDelta = 1.0 / 9.0;

                float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
                float yPixelOffset = 1.0 / shadowMapSize[ i ].y;

                float dx0 = -1.25 * xPixelOffset;
                float dy0 = -1.25 * yPixelOffset;
                float dx1 = 1.25 * xPixelOffset;
                float dy1 = 1.25 * yPixelOffset;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );

            #elif defined( SHADOWMAP_TYPE_PCF_SOFT )

                        // Percentage-close filtering
                        // (9 pixel kernel)
                        // http://fabiensanglard.net/shadowmappingPCF/
                float shadow = 0.0;

                float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
                float yPixelOffset = 1.0 / shadowMapSize[ i ].y;

                float dx0 = -1.0 * xPixelOffset;
                float dy0 = -1.0 * yPixelOffset;
                float dx1 = 1.0 * xPixelOffset;
                float dy1 = 1.0 * yPixelOffset;

                mat3 shadowKernel;
                mat3 depthKernel;

                depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
                depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
                depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
                depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
                depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
                depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
                depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
                depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
                depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );

                vec3 shadowZ = vec3( shadowCoord.z );
                shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));
                shadowKernel[0] *= vec3(0.25);

                shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));
                shadowKernel[1] *= vec3(0.25);

                shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));
                shadowKernel[2] *= vec3(0.25);

                vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );

                shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );
                shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );

                vec4 shadowValues;
                shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );
                shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );
                shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );
                shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );

                shadow = dot( shadowValues, vec4( 1.0 ) );

                shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );

            #else

                vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );
                float fDepth = unpackDepth( rgbaDepth );

                if ( fDepth < shadowCoord.z )

        // spot with multiple shadows is darker

                    shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );

        // spot with multiple shadows has the same color as single shadow spot

        //                     shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );

            #endif

        }


        #ifdef SHADOWMAP_DEBUG

            #ifdef SHADOWMAP_CASCADE

                if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];

            #else

                if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];

            #endif

        #endif

    }

    #ifdef GAMMA_OUTPUT

        shadowColor *= shadowColor;

    #endif

    gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;

#endif

#ifdef GAMMA_OUTPUT

    gl_FragColor.xyz = sqrt( gl_FragColor.xyz );

#endif
#ifdef USE_FOG

    #ifdef USE_LOGDEPTHBUF_EXT

        float depth = gl_FragDepthEXT / gl_FragCoord.w;

    #else

        float depth = gl_FragCoord.z / gl_FragCoord.w;

    #endif

    #ifdef FOG_EXP2

        const float LOG2 = 1.442695;
        float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );
        fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );

    #else

        float fogFactor = smoothstep( fogNear, fogFar, depth );

    #endif

    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );

#endif
}

MeshLambertMaterial

Vertex Shader

#define LAMBERT
varying vec3 vLightFront;
#ifdef DOUBLE_SIDED
    varying vec3 vLightBack;
#endif
#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    varying vec2 vUv;
    uniform vec4 offsetRepeat;

#endif

#ifdef USE_LIGHTMAP

    varying vec2 vUv2;

#endif
#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )

    varying vec3 vReflect;

    uniform float refractionRatio;

#endif

uniform vec3 ambient;
uniform vec3 diffuse;
uniform vec3 emissive;

uniform vec3 ambientLightColor;

#if MAX_DIR_LIGHTS > 0

    uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];
    uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];

#endif

#if MAX_HEMI_LIGHTS > 0

    uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];
    uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];
    uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];

#endif

#if MAX_POINT_LIGHTS > 0

    uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];
    uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];
    uniform float pointLightDistance[ MAX_POINT_LIGHTS ];

#endif

#if MAX_SPOT_LIGHTS > 0

    uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];
    uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];
    uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];
    uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];
    uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];
    uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];

#endif

#ifdef WRAP_AROUND

    uniform vec3 wrapRGB;

#endif

#ifdef USE_COLOR

    varying vec3 vColor;

#endif
#ifdef USE_MORPHTARGETS

    #ifndef USE_MORPHNORMALS

    uniform float morphTargetInfluences[ 8 ];

    #else

    uniform float morphTargetInfluences[ 4 ];

    #endif

#endif
#ifdef USE_SKINNING

    uniform mat4 bindMatrix;
    uniform mat4 bindMatrixInverse;

    #ifdef BONE_TEXTURE

        uniform sampler2D boneTexture;
        uniform int boneTextureWidth;
        uniform int boneTextureHeight;

        mat4 getBoneMatrix( const in float i ) {

            float j = i * 4.0;
            float x = mod( j, float( boneTextureWidth ) );
            float y = floor( j / float( boneTextureWidth ) );

            float dx = 1.0 / float( boneTextureWidth );
            float dy = 1.0 / float( boneTextureHeight );

            y = dy * ( y + 0.5 );

            vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );
            vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );
            vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );
            vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );

            mat4 bone = mat4( v1, v2, v3, v4 );

            return bone;

        }

    #else

        uniform mat4 boneGlobalMatrices[ MAX_BONES ];

        mat4 getBoneMatrix( const in float i ) {

            mat4 bone = boneGlobalMatrices[ int(i) ];
            return bone;

        }

    #endif

#endif

#ifdef USE_SHADOWMAP

    varying vec4 vShadowCoord[ MAX_SHADOWS ];
    uniform mat4 shadowMatrix[ MAX_SHADOWS ];

#endif
#ifdef USE_LOGDEPTHBUF

    #ifdef USE_LOGDEPTHBUF_EXT

        varying float vFragDepth;

    #endif

    uniform float logDepthBufFC;

#endif
void main() {
#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    vUv = uv * offsetRepeat.zw + offsetRepeat.xy;

#endif
#ifdef USE_LIGHTMAP

    vUv2 = uv2;

#endif
#ifdef USE_COLOR

    #ifdef GAMMA_INPUT

        vColor = color * color;

    #else

        vColor = color;

    #endif

#endif
#ifdef USE_MORPHNORMALS

    vec3 morphedNormal = vec3( 0.0 );

    morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];
    morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];
    morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];
    morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];

    morphedNormal += normal;

#endif
#ifdef USE_SKINNING

    mat4 boneMatX = getBoneMatrix( skinIndex.x );
    mat4 boneMatY = getBoneMatrix( skinIndex.y );
    mat4 boneMatZ = getBoneMatrix( skinIndex.z );
    mat4 boneMatW = getBoneMatrix( skinIndex.w );

#endif
#ifdef USE_SKINNING

    mat4 skinMatrix = mat4( 0.0 );
    skinMatrix += skinWeight.x * boneMatX;
    skinMatrix += skinWeight.y * boneMatY;
    skinMatrix += skinWeight.z * boneMatZ;
    skinMatrix += skinWeight.w * boneMatW;
    skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;

    #ifdef USE_MORPHNORMALS

    vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );

    #else

    vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );

    #endif

#endif

#ifdef USE_SKINNING

    vec3 objectNormal = skinnedNormal.xyz;

#elif defined( USE_MORPHNORMALS )

    vec3 objectNormal = morphedNormal;

#else

    vec3 objectNormal = normal;

#endif

#ifdef FLIP_SIDED

    objectNormal = -objectNormal;

#endif

vec3 transformedNormal = normalMatrix * objectNormal;

#ifdef USE_MORPHTARGETS

    vec3 morphed = vec3( 0.0 );
    morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];
    morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];
    morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];
    morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];

    #ifndef USE_MORPHNORMALS

    morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];
    morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];
    morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];
    morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];

    #endif

    morphed += position;

#endif
#ifdef USE_SKINNING

    #ifdef USE_MORPHTARGETS

    vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );

    #else

    vec4 skinVertex = bindMatrix * vec4( position, 1.0 );

    #endif

    vec4 skinned = vec4( 0.0 );
    skinned += boneMatX * skinVertex * skinWeight.x;
    skinned += boneMatY * skinVertex * skinWeight.y;
    skinned += boneMatZ * skinVertex * skinWeight.z;
    skinned += boneMatW * skinVertex * skinWeight.w;
    skinned  = bindMatrixInverse * skinned;

#endif

#ifdef USE_SKINNING

    vec4 mvPosition = modelViewMatrix * skinned;

#elif defined( USE_MORPHTARGETS )

    vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 );

#else

    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

#endif

gl_Position = projectionMatrix * mvPosition;

#ifdef USE_LOGDEPTHBUF

    gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;

    #ifdef USE_LOGDEPTHBUF_EXT

        vFragDepth = 1.0 + gl_Position.w;

#else

        gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;

    #endif

#endif
#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )

    #ifdef USE_SKINNING

        vec4 worldPosition = modelMatrix * skinned;

    #elif defined( USE_MORPHTARGETS )

        vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );

    #else

        vec4 worldPosition = modelMatrix * vec4( position, 1.0 );

    #endif

#endif

#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )

    vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;
    worldNormal = normalize( worldNormal );

    vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );

    #ifdef ENVMAP_MODE_REFLECTION

        vReflect = reflect( cameraToVertex, worldNormal );

    #else

        vReflect = refract( cameraToVertex, worldNormal, refractionRatio );

    #endif

#endif

vLightFront = vec3( 0.0 );

#ifdef DOUBLE_SIDED

    vLightBack = vec3( 0.0 );

#endif

transformedNormal = normalize( transformedNormal );

#if MAX_DIR_LIGHTS > 0

for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {

    vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );
    vec3 dirVector = normalize( lDirection.xyz );

    float dotProduct = dot( transformedNormal, dirVector );
    vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );

    #ifdef DOUBLE_SIDED

        vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );

        #ifdef WRAP_AROUND

            vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );

        #endif

    #endif

    #ifdef WRAP_AROUND

        vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );
        directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );

        #ifdef DOUBLE_SIDED

            directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );

        #endif

    #endif

    vLightFront += directionalLightColor[ i ] * directionalLightWeighting;

    #ifdef DOUBLE_SIDED

        vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;

    #endif

}

#endif

#if MAX_POINT_LIGHTS > 0

    for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {

        vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );
        vec3 lVector = lPosition.xyz - mvPosition.xyz;

        float lDistance = 1.0;
        if ( pointLightDistance[ i ] > 0.0 )
            lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );

        lVector = normalize( lVector );
        float dotProduct = dot( transformedNormal, lVector );

        vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );

        #ifdef DOUBLE_SIDED

            vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );

            #ifdef WRAP_AROUND

                vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );

            #endif

        #endif

        #ifdef WRAP_AROUND

            vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );
            pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );

            #ifdef DOUBLE_SIDED

                pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );

            #endif

        #endif

        vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;

        #ifdef DOUBLE_SIDED

            vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;

        #endif

    }

#endif

#if MAX_SPOT_LIGHTS > 0

    for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {

        vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );
        vec3 lVector = lPosition.xyz - mvPosition.xyz;

        float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );

        if ( spotEffect > spotLightAngleCos[ i ] ) {

            spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );

            float lDistance = 1.0;
            if ( spotLightDistance[ i ] > 0.0 )
                lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );

            lVector = normalize( lVector );

            float dotProduct = dot( transformedNormal, lVector );
            vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );

            #ifdef DOUBLE_SIDED

                vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );

                #ifdef WRAP_AROUND

                    vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );

                #endif

            #endif

            #ifdef WRAP_AROUND

                vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );
                spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );

                #ifdef DOUBLE_SIDED

                    spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );

                #endif

            #endif

            vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;

            #ifdef DOUBLE_SIDED

                vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;

            #endif

        }

    }

#endif

#if MAX_HEMI_LIGHTS > 0

    for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {

        vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );
        vec3 lVector = normalize( lDirection.xyz );

        float dotProduct = dot( transformedNormal, lVector );

        float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;
        float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;

        vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );

        #ifdef DOUBLE_SIDED

            vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );

        #endif

    }

#endif

vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;

#ifdef DOUBLE_SIDED

    vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;

#endif
#ifdef USE_SHADOWMAP

    for( int i = 0; i < MAX_SHADOWS; i ++ ) {

        vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;

    }

#endif
}

Fragment Shader

uniform float opacity;
varying vec3 vLightFront;
#ifdef DOUBLE_SIDED
    varying vec3 vLightBack;
#endif
#ifdef USE_COLOR

    varying vec3 vColor;

#endif

#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    varying vec2 vUv;

#endif

#ifdef USE_MAP

    uniform sampler2D map;

#endif
#ifdef USE_ALPHAMAP

    uniform sampler2D alphaMap;

#endif

#ifdef USE_LIGHTMAP

    varying vec2 vUv2;
    uniform sampler2D lightMap;

#endif
#ifdef USE_ENVMAP

    uniform float reflectivity;
    #ifdef ENVMAP_TYPE_CUBE
        uniform samplerCube envMap;
    #else
        uniform sampler2D envMap;
    #endif
    uniform float flipEnvMap;

    #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )

        uniform float refractionRatio;

    #else

        varying vec3 vReflect;

    #endif

#endif

#ifdef USE_FOG

    uniform vec3 fogColor;

    #ifdef FOG_EXP2

        uniform float fogDensity;

    #else

        uniform float fogNear;
        uniform float fogFar;
    #endif

#endif
#ifdef USE_SHADOWMAP

    uniform sampler2D shadowMap[ MAX_SHADOWS ];
    uniform vec2 shadowMapSize[ MAX_SHADOWS ];

    uniform float shadowDarkness[ MAX_SHADOWS ];
    uniform float shadowBias[ MAX_SHADOWS ];

    varying vec4 vShadowCoord[ MAX_SHADOWS ];

    float unpackDepth( const in vec4 rgba_depth ) {

        const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );
        float depth = dot( rgba_depth, bit_shift );
        return depth;

    }

#endif
#ifdef USE_SPECULARMAP

    uniform sampler2D specularMap;

#endif
#ifdef USE_LOGDEPTHBUF

    uniform float logDepthBufFC;

    #ifdef USE_LOGDEPTHBUF_EXT

        #extension GL_EXT_frag_depth : enable
        varying float vFragDepth;

    #endif

#endif
void main() {
    gl_FragColor = vec4( vec3( 1.0 ), opacity );
#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)

    gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;

#endif
#ifdef USE_MAP

    vec4 texelColor = texture2D( map, vUv );

    #ifdef GAMMA_INPUT

        texelColor.xyz *= texelColor.xyz;

    #endif

    gl_FragColor = gl_FragColor * texelColor;

#endif
#ifdef USE_ALPHAMAP

    gl_FragColor.a *= texture2D( alphaMap, vUv ).g;

#endif

#ifdef ALPHATEST

    if ( gl_FragColor.a < ALPHATEST ) discard;

#endif

float specularStrength;

#ifdef USE_SPECULARMAP

    vec4 texelSpecular = texture2D( specularMap, vUv );
    specularStrength = texelSpecular.r;

#else

    specularStrength = 1.0;

#endif
    #ifdef DOUBLE_SIDED
        if ( gl_FrontFacing )
            gl_FragColor.xyz *= vLightFront;
        else
            gl_FragColor.xyz *= vLightBack;
    #else
        gl_FragColor.xyz *= vLightFront;
    #endif
#ifdef USE_LIGHTMAP

    gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );

#endif
#ifdef USE_COLOR

    gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );

#endif
#ifdef USE_ENVMAP

    #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )

        vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );

        // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transforma...        // Transforming Normal Vectors with the Inverse Transformation

        vec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );

        #ifdef ENVMAP_MODE_REFLECTION

            vec3 reflectVec = reflect( cameraToVertex, worldNormal );

        #else

            vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );

        #endif

    #else

        vec3 reflectVec = vReflect;

    #endif

    #ifdef DOUBLE_SIDED
        float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );
    #else
        float flipNormal = 1.0;
    #endif

    #ifdef ENVMAP_TYPE_CUBE
        vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );

    #elif defined( ENVMAP_TYPE_EQUIREC )
        vec2 sampleUV;
        sampleUV.y = clamp( flipNormal * reflectVec.y * 0.5 + 0.5, 0.0, 1.0);
        sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * 0.15915494309189533576888376337251 + 0.5; // reciprocal( 2 PI ) + 0.5
        vec4 envColor = texture2D( envMap, sampleUV );

    #elif defined( ENVMAP_TYPE_SPHERE )
        vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));
        vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );
    #endif

    #ifdef GAMMA_INPUT

        envColor.xyz *= envColor.xyz;

    #endif

    #ifdef ENVMAP_BLENDING_MULTIPLY

        gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * envColor.xyz, specularStrength * reflectivity );

    #elif defined( ENVMAP_BLENDING_MIX )

        gl_FragColor.xyz = mix( gl_FragColor.xyz, envColor.xyz, specularStrength * reflectivity );

    #elif defined( ENVMAP_BLENDING_ADD )

        gl_FragColor.xyz += envColor.xyz * specularStrength * reflectivity;

    #endif

#endif

#ifdef USE_SHADOWMAP

    #ifdef SHADOWMAP_DEBUG

        vec3 frustumColors[3];
        frustumColors[0] = vec3( 1.0, 0.5, 0.0 );
        frustumColors[1] = vec3( 0.0, 1.0, 0.8 );
        frustumColors[2] = vec3( 0.0, 0.5, 1.0 );

    #endif

    #ifdef SHADOWMAP_CASCADE

        int inFrustumCount = 0;

    #endif

    float fDepth;
    vec3 shadowColor = vec3( 1.0 );

    for( int i = 0; i < MAX_SHADOWS; i ++ ) {

        vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;

                // if ( something && something ) breaks ATI OpenGL shader compiler
                // if ( all( something, something ) ) using this instead

        bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );
        bool inFrustum = all( inFrustumVec );

                // don't shadow pixels outside of light frustum
                // use just first frustum (for cascades)
                // don't shadow pixels behind far plane of light frustum

        #ifdef SHADOWMAP_CASCADE

            inFrustumCount += int( inFrustum );
            bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );

        #else

            bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );

        #endif

        bool frustumTest = all( frustumTestVec );

        if ( frustumTest ) {

            shadowCoord.z += shadowBias[ i ];

            #if defined( SHADOWMAP_TYPE_PCF )

                        // Percentage-close filtering
                        // (9 pixel kernel)
                        // http://fabiensanglard.net/shadowmappingPCF/
                float shadow = 0.0;

        /*
                        // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
                        // must enroll loop manually

                for ( float y = -1.25; y <= 1.25; y += 1.25 )
                    for ( float x = -1.25; x <= 1.25; x += 1.25 ) {

                        vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );

                                // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup
                                //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );

                        float fDepth = unpackDepth( rgbaDepth );

                        if ( fDepth < shadowCoord.z )
                            shadow += 1.0;

                }

                shadow /= 9.0;

        */

                const float shadowDelta = 1.0 / 9.0;

                float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
                float yPixelOffset = 1.0 / shadowMapSize[ i ].y;

                float dx0 = -1.25 * xPixelOffset;
                float dy0 = -1.25 * yPixelOffset;
                float dx1 = 1.25 * xPixelOffset;
                float dy1 = 1.25 * yPixelOffset;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );

            #elif defined( SHADOWMAP_TYPE_PCF_SOFT )

                        // Percentage-close filtering
                        // (9 pixel kernel)
                        // http://fabiensanglard.net/shadowmappingPCF/
                float shadow = 0.0;

                float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
                float yPixelOffset = 1.0 / shadowMapSize[ i ].y;

                float dx0 = -1.0 * xPixelOffset;
                float dy0 = -1.0 * yPixelOffset;
                float dx1 = 1.0 * xPixelOffset;
                float dy1 = 1.0 * yPixelOffset;

                mat3 shadowKernel;
                mat3 depthKernel;

                depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
                depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
                depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
                depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
                depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
                depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
                depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
                depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
                depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );

                vec3 shadowZ = vec3( shadowCoord.z );
                shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));
                shadowKernel[0] *= vec3(0.25);

                shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));
                shadowKernel[1] *= vec3(0.25);

                shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));
                shadowKernel[2] *= vec3(0.25);

                vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );

                shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );
                shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );

                vec4 shadowValues;
                shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );
                shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );
                shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );
                shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );

                shadow = dot( shadowValues, vec4( 1.0 ) );

                shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );

            #else

                vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );
                float fDepth = unpackDepth( rgbaDepth );

                if ( fDepth < shadowCoord.z )

        // spot with multiple shadows is darker

                    shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );

        // spot with multiple shadows has the same color as single shadow spot

        //                     shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );

            #endif

        }


        #ifdef SHADOWMAP_DEBUG

            #ifdef SHADOWMAP_CASCADE

                if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];

            #else

                if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];

            #endif

        #endif

    }

    #ifdef GAMMA_OUTPUT

        shadowColor *= shadowColor;

    #endif

    gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;

#endif

#ifdef GAMMA_OUTPUT

    gl_FragColor.xyz = sqrt( gl_FragColor.xyz );

#endif
#ifdef USE_FOG

    #ifdef USE_LOGDEPTHBUF_EXT

        float depth = gl_FragDepthEXT / gl_FragCoord.w;

    #else

        float depth = gl_FragCoord.z / gl_FragCoord.w;

    #endif

    #ifdef FOG_EXP2

        const float LOG2 = 1.442695;
        float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );
        fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );

    #else

        float fogFactor = smoothstep( fogNear, fogFar, depth );

    #endif

    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );

#endif
}

MeshPhongMaterial

Vertex Shader

#define PHONG
varying vec3 vViewPosition;
varying vec3 vNormal;
#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    varying vec2 vUv;
    uniform vec4 offsetRepeat;

#endif

#ifdef USE_LIGHTMAP

    varying vec2 vUv2;

#endif
#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )

    varying vec3 vReflect;

    uniform float refractionRatio;

#endif

#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )

    varying vec3 vWorldPosition;

#endif

#ifdef USE_COLOR

    varying vec3 vColor;

#endif
#ifdef USE_MORPHTARGETS

    #ifndef USE_MORPHNORMALS

    uniform float morphTargetInfluences[ 8 ];

    #else

    uniform float morphTargetInfluences[ 4 ];

    #endif

#endif
#ifdef USE_SKINNING

    uniform mat4 bindMatrix;
    uniform mat4 bindMatrixInverse;

    #ifdef BONE_TEXTURE

        uniform sampler2D boneTexture;
        uniform int boneTextureWidth;
        uniform int boneTextureHeight;

        mat4 getBoneMatrix( const in float i ) {

            float j = i * 4.0;
            float x = mod( j, float( boneTextureWidth ) );
            float y = floor( j / float( boneTextureWidth ) );

            float dx = 1.0 / float( boneTextureWidth );
            float dy = 1.0 / float( boneTextureHeight );

            y = dy * ( y + 0.5 );

            vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );
            vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );
            vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );
            vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );

            mat4 bone = mat4( v1, v2, v3, v4 );

            return bone;

        }

    #else

        uniform mat4 boneGlobalMatrices[ MAX_BONES ];

        mat4 getBoneMatrix( const in float i ) {

            mat4 bone = boneGlobalMatrices[ int(i) ];
            return bone;

        }

    #endif

#endif

#ifdef USE_SHADOWMAP

    varying vec4 vShadowCoord[ MAX_SHADOWS ];
    uniform mat4 shadowMatrix[ MAX_SHADOWS ];

#endif
#ifdef USE_LOGDEPTHBUF

    #ifdef USE_LOGDEPTHBUF_EXT

        varying float vFragDepth;

    #endif

    uniform float logDepthBufFC;

#endif
void main() {
#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    vUv = uv * offsetRepeat.zw + offsetRepeat.xy;

#endif
#ifdef USE_LIGHTMAP

    vUv2 = uv2;

#endif
#ifdef USE_COLOR

    #ifdef GAMMA_INPUT

        vColor = color * color;

    #else

        vColor = color;

    #endif

#endif
#ifdef USE_MORPHNORMALS

    vec3 morphedNormal = vec3( 0.0 );

    morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];
    morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];
    morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];
    morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];

    morphedNormal += normal;

#endif
#ifdef USE_SKINNING

    mat4 boneMatX = getBoneMatrix( skinIndex.x );
    mat4 boneMatY = getBoneMatrix( skinIndex.y );
    mat4 boneMatZ = getBoneMatrix( skinIndex.z );
    mat4 boneMatW = getBoneMatrix( skinIndex.w );

#endif
#ifdef USE_SKINNING

    mat4 skinMatrix = mat4( 0.0 );
    skinMatrix += skinWeight.x * boneMatX;
    skinMatrix += skinWeight.y * boneMatY;
    skinMatrix += skinWeight.z * boneMatZ;
    skinMatrix += skinWeight.w * boneMatW;
    skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;

    #ifdef USE_MORPHNORMALS

    vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );

    #else

    vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );

    #endif

#endif

#ifdef USE_SKINNING

    vec3 objectNormal = skinnedNormal.xyz;

#elif defined( USE_MORPHNORMALS )

    vec3 objectNormal = morphedNormal;

#else

    vec3 objectNormal = normal;

#endif

#ifdef FLIP_SIDED

    objectNormal = -objectNormal;

#endif

vec3 transformedNormal = normalMatrix * objectNormal;

    vNormal = normalize( transformedNormal );
#ifdef USE_MORPHTARGETS

    vec3 morphed = vec3( 0.0 );
    morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];
    morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];
    morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];
    morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];

    #ifndef USE_MORPHNORMALS

    morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];
    morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];
    morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];
    morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];

    #endif

    morphed += position;

#endif
#ifdef USE_SKINNING

    #ifdef USE_MORPHTARGETS

    vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );

    #else

    vec4 skinVertex = bindMatrix * vec4( position, 1.0 );

    #endif

    vec4 skinned = vec4( 0.0 );
    skinned += boneMatX * skinVertex * skinWeight.x;
    skinned += boneMatY * skinVertex * skinWeight.y;
    skinned += boneMatZ * skinVertex * skinWeight.z;
    skinned += boneMatW * skinVertex * skinWeight.w;
    skinned  = bindMatrixInverse * skinned;

#endif

#ifdef USE_SKINNING

    vec4 mvPosition = modelViewMatrix * skinned;

#elif defined( USE_MORPHTARGETS )

    vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 );

#else

    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

#endif

gl_Position = projectionMatrix * mvPosition;

#ifdef USE_LOGDEPTHBUF

    gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;

    #ifdef USE_LOGDEPTHBUF_EXT

        vFragDepth = 1.0 + gl_Position.w;

#else

        gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;

    #endif

#endif
    vViewPosition = -mvPosition.xyz;
#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )

    #ifdef USE_SKINNING

        vec4 worldPosition = modelMatrix * skinned;

    #elif defined( USE_MORPHTARGETS )

        vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );

    #else

        vec4 worldPosition = modelMatrix * vec4( position, 1.0 );

    #endif

#endif

#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )

    vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;
    worldNormal = normalize( worldNormal );

    vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );

    #ifdef ENVMAP_MODE_REFLECTION

        vReflect = reflect( cameraToVertex, worldNormal );

    #else

        vReflect = refract( cameraToVertex, worldNormal, refractionRatio );

    #endif

#endif

#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )

    vWorldPosition = worldPosition.xyz;

#endif
#ifdef USE_SHADOWMAP

    for( int i = 0; i < MAX_SHADOWS; i ++ ) {

        vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;

    }

#endif
}

Fragment Shader

#define PHONG
uniform vec3 diffuse;
uniform float opacity;
uniform vec3 ambient;
uniform vec3 emissive;
uniform vec3 specular;
uniform float shininess;
#ifdef USE_COLOR

    varying vec3 vColor;

#endif

#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )

    varying vec2 vUv;

#endif

#ifdef USE_MAP

    uniform sampler2D map;

#endif
#ifdef USE_ALPHAMAP

    uniform sampler2D alphaMap;

#endif

#ifdef USE_LIGHTMAP

    varying vec2 vUv2;
    uniform sampler2D lightMap;

#endif
#ifdef USE_ENVMAP

    uniform float reflectivity;
    #ifdef ENVMAP_TYPE_CUBE
        uniform samplerCube envMap;
    #else
        uniform sampler2D envMap;
    #endif
    uniform float flipEnvMap;

    #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )

        uniform float refractionRatio;

    #else

        varying vec3 vReflect;

    #endif

#endif

#ifdef USE_FOG

    uniform vec3 fogColor;

    #ifdef FOG_EXP2

        uniform float fogDensity;

    #else

        uniform float fogNear;
        uniform float fogFar;
    #endif

#endif
uniform vec3 ambientLightColor;

#if MAX_DIR_LIGHTS > 0

    uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];
    uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];

#endif

#if MAX_HEMI_LIGHTS > 0

    uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];
    uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];
    uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];

#endif

#if MAX_POINT_LIGHTS > 0

    uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];

    uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];
    uniform float pointLightDistance[ MAX_POINT_LIGHTS ];

#endif

#if MAX_SPOT_LIGHTS > 0

    uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];
    uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];
    uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];
    uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];
    uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];

    uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];

#endif

#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )

    varying vec3 vWorldPosition;

#endif

#ifdef WRAP_AROUND

    uniform vec3 wrapRGB;

#endif

varying vec3 vViewPosition;
varying vec3 vNormal;
#ifdef USE_SHADOWMAP

    uniform sampler2D shadowMap[ MAX_SHADOWS ];
    uniform vec2 shadowMapSize[ MAX_SHADOWS ];

    uniform float shadowDarkness[ MAX_SHADOWS ];
    uniform float shadowBias[ MAX_SHADOWS ];

    varying vec4 vShadowCoord[ MAX_SHADOWS ];

    float unpackDepth( const in vec4 rgba_depth ) {

        const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );
        float depth = dot( rgba_depth, bit_shift );
        return depth;

    }

#endif
#ifdef USE_BUMPMAP

    uniform sampler2D bumpMap;
    uniform float bumpScale;

            // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen
            //    http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html
            // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)

    vec2 dHdxy_fwd() {

        vec2 dSTdx = dFdx( vUv );
        vec2 dSTdy = dFdy( vUv );

        float Hll = bumpScale * texture2D( bumpMap, vUv ).x;
        float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;
        float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;

        return vec2( dBx, dBy );

    }

    vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {

        vec3 vSigmaX = dFdx( surf_pos );
        vec3 vSigmaY = dFdy( surf_pos );
        vec3 vN = surf_norm;        // normalized

        vec3 R1 = cross( vSigmaY, vN );
        vec3 R2 = cross( vN, vSigmaX );

        float fDet = dot( vSigmaX, R1 );

        vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
        return normalize( abs( fDet ) * surf_norm - vGrad );

    }

#endif
#ifdef USE_NORMALMAP

    uniform sampler2D normalMap;
    uniform vec2 normalScale;

            // Per-Pixel Tangent Space Normal Mapping
            // hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping . html

    vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {

        vec3 q0 = dFdx( eye_pos.xyz );
        vec3 q1 = dFdy( eye_pos.xyz );
        vec2 st0 = dFdx( vUv.st );
        vec2 st1 = dFdy( vUv.st );

        vec3 S = normalize( q0 * st1.t - q1 * st0.t );
        vec3 T = normalize( -q0 * st1.s + q1 * st0.s );
        vec3 N = normalize( surf_norm );

        vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
        mapN.xy = normalScale * mapN.xy;
        mat3 tsn = mat3( S, T, N );
        return normalize( tsn * mapN );

    }

#endif

#ifdef USE_SPECULARMAP

    uniform sampler2D specularMap;

#endif
#ifdef USE_LOGDEPTHBUF

    uniform float logDepthBufFC;

    #ifdef USE_LOGDEPTHBUF_EXT

        #extension GL_EXT_frag_depth : enable
        varying float vFragDepth;

    #endif

#endif
void main() {
    gl_FragColor = vec4( vec3( 1.0 ), opacity );
#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)

    gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;

#endif
#ifdef USE_MAP

    vec4 texelColor = texture2D( map, vUv );

    #ifdef GAMMA_INPUT

        texelColor.xyz *= texelColor.xyz;

    #endif

    gl_FragColor = gl_FragColor * texelColor;

#endif
#ifdef USE_ALPHAMAP

    gl_FragColor.a *= texture2D( alphaMap, vUv ).g;

#endif

#ifdef ALPHATEST

    if ( gl_FragColor.a < ALPHATEST ) discard;

#endif

float specularStrength;

#ifdef USE_SPECULARMAP

    vec4 texelSpecular = texture2D( specularMap, vUv );
    specularStrength = texelSpecular.r;

#else

    specularStrength = 1.0;

#endif
vec3 normal = normalize( vNormal );
vec3 viewPosition = normalize( vViewPosition );

#ifdef DOUBLE_SIDED

    normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );

#endif

#ifdef USE_NORMALMAP

    normal = perturbNormal2Arb( -vViewPosition, normal );

#elif defined( USE_BUMPMAP )

    normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );

#endif

#if MAX_POINT_LIGHTS > 0

    vec3 pointDiffuse = vec3( 0.0 );
    vec3 pointSpecular = vec3( 0.0 );

    for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {

        vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );
        vec3 lVector = lPosition.xyz + vViewPosition.xyz;

        float lDistance = 1.0;
        if ( pointLightDistance[ i ] > 0.0 )
            lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );

        lVector = normalize( lVector );

                // diffuse

        float dotProduct = dot( normal, lVector );

        #ifdef WRAP_AROUND

            float pointDiffuseWeightFull = max( dotProduct, 0.0 );
            float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );

            vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );

        #else

            float pointDiffuseWeight = max( dotProduct, 0.0 );

        #endif

        pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;

                // specular

        vec3 pointHalfVector = normalize( lVector + viewPosition );
        float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );
        float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );

        float specularNormalization = ( shininess + 2.0 ) / 8.0;

        vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );
        pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;

    }

#endif

#if MAX_SPOT_LIGHTS > 0

    vec3 spotDiffuse = vec3( 0.0 );
    vec3 spotSpecular = vec3( 0.0 );

    for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {

        vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );
        vec3 lVector = lPosition.xyz + vViewPosition.xyz;

        float lDistance = 1.0;
        if ( spotLightDistance[ i ] > 0.0 )
            lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );

        lVector = normalize( lVector );

        float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );

        if ( spotEffect > spotLightAngleCos[ i ] ) {

            spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );

                    // diffuse

            float dotProduct = dot( normal, lVector );

            #ifdef WRAP_AROUND

                float spotDiffuseWeightFull = max( dotProduct, 0.0 );
                float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );

                vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );

            #else

                float spotDiffuseWeight = max( dotProduct, 0.0 );

            #endif

            spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;

                    // specular

            vec3 spotHalfVector = normalize( lVector + viewPosition );
            float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );
            float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );

            float specularNormalization = ( shininess + 2.0 ) / 8.0;

            vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );
            spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;

        }

    }

#endif

#if MAX_DIR_LIGHTS > 0

    vec3 dirDiffuse = vec3( 0.0 );
    vec3 dirSpecular = vec3( 0.0 );

    for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {

        vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );
        vec3 dirVector = normalize( lDirection.xyz );

                // diffuse

        float dotProduct = dot( normal, dirVector );

        #ifdef WRAP_AROUND

            float dirDiffuseWeightFull = max( dotProduct, 0.0 );
            float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );

            vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );

        #else

            float dirDiffuseWeight = max( dotProduct, 0.0 );

        #endif

        dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;

        // specular

        vec3 dirHalfVector = normalize( dirVector + viewPosition );
        float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );
        float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );

        /*
        // fresnel term from skin shader
        const float F0 = 0.128;

        float base = 1.0 - dot( viewPosition, dirHalfVector );
        float exponential = pow( base, 5.0 );

        float fresnel = exponential + F0 * ( 1.0 - exponential );
        */

        /*
        // fresnel term from fresnel shader
        const float mFresnelBias = 0.08;
        const float mFresnelScale = 0.3;
        const float mFresnelPower = 5.0;

        float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );
        */

        float specularNormalization = ( shininess + 2.0 ) / 8.0;

        //         dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;

        vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );
        dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;


    }

#endif

#if MAX_HEMI_LIGHTS > 0

    vec3 hemiDiffuse = vec3( 0.0 );
    vec3 hemiSpecular = vec3( 0.0 );

    for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {

        vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );
        vec3 lVector = normalize( lDirection.xyz );

        // diffuse

        float dotProduct = dot( normal, lVector );
        float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;

        vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );

        hemiDiffuse += diffuse * hemiColor;

        // specular (sky light)

        vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );
        float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;
        float hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );

        // specular (ground light)

        vec3 lVectorGround = -lVector;

        vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );
        float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;
        float hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );

        float dotProductGround = dot( normal, lVectorGround );

        float specularNormalization = ( shininess + 2.0 ) / 8.0;

        vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );
        vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );
        hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );

    }

#endif

vec3 totalDiffuse = vec3( 0.0 );
vec3 totalSpecular = vec3( 0.0 );

#if MAX_DIR_LIGHTS > 0

    totalDiffuse += dirDiffuse;
    totalSpecular += dirSpecular;

#endif

#if MAX_HEMI_LIGHTS > 0

    totalDiffuse += hemiDiffuse;
    totalSpecular += hemiSpecular;

#endif

#if MAX_POINT_LIGHTS > 0

    totalDiffuse += pointDiffuse;
    totalSpecular += pointSpecular;

#endif

#if MAX_SPOT_LIGHTS > 0

    totalDiffuse += spotDiffuse;
    totalSpecular += spotSpecular;

#endif

#ifdef METAL

    gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );

#else

    gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;

#endif
#ifdef USE_LIGHTMAP

    gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );

#endif
#ifdef USE_COLOR

    gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );

#endif
#ifdef USE_ENVMAP

    #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )

        vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );

        // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transforma...        // Transforming Normal Vectors with the Inverse Transformation

        vec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );

        #ifdef ENVMAP_MODE_REFLECTION

            vec3 reflectVec = reflect( cameraToVertex, worldNormal );

        #else

            vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );

        #endif

    #else

        vec3 reflectVec = vReflect;

    #endif

    #ifdef DOUBLE_SIDED
        float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );
    #else
        float flipNormal = 1.0;
    #endif

    #ifdef ENVMAP_TYPE_CUBE
        vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );

    #elif defined( ENVMAP_TYPE_EQUIREC )
        vec2 sampleUV;
        sampleUV.y = clamp( flipNormal * reflectVec.y * 0.5 + 0.5, 0.0, 1.0);
        sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * 0.15915494309189533576888376337251 + 0.5; // reciprocal( 2 PI ) + 0.5
        vec4 envColor = texture2D( envMap, sampleUV );

    #elif defined( ENVMAP_TYPE_SPHERE )
        vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));
        vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );
    #endif

    #ifdef GAMMA_INPUT

        envColor.xyz *= envColor.xyz;

    #endif

    #ifdef ENVMAP_BLENDING_MULTIPLY

        gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * envColor.xyz, specularStrength * reflectivity );

    #elif defined( ENVMAP_BLENDING_MIX )

        gl_FragColor.xyz = mix( gl_FragColor.xyz, envColor.xyz, specularStrength * reflectivity );

    #elif defined( ENVMAP_BLENDING_ADD )

        gl_FragColor.xyz += envColor.xyz * specularStrength * reflectivity;

    #endif

#endif

#ifdef USE_SHADOWMAP

    #ifdef SHADOWMAP_DEBUG

        vec3 frustumColors[3];
        frustumColors[0] = vec3( 1.0, 0.5, 0.0 );
        frustumColors[1] = vec3( 0.0, 1.0, 0.8 );
        frustumColors[2] = vec3( 0.0, 0.5, 1.0 );

    #endif

    #ifdef SHADOWMAP_CASCADE

        int inFrustumCount = 0;

    #endif

    float fDepth;
    vec3 shadowColor = vec3( 1.0 );

    for( int i = 0; i < MAX_SHADOWS; i ++ ) {

        vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;

                // if ( something && something ) breaks ATI OpenGL shader compiler
                // if ( all( something, something ) ) using this instead

        bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );
        bool inFrustum = all( inFrustumVec );

                // don't shadow pixels outside of light frustum
                // use just first frustum (for cascades)
                // don't shadow pixels behind far plane of light frustum

        #ifdef SHADOWMAP_CASCADE

            inFrustumCount += int( inFrustum );
            bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );

        #else

            bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );

        #endif

        bool frustumTest = all( frustumTestVec );

        if ( frustumTest ) {

            shadowCoord.z += shadowBias[ i ];

            #if defined( SHADOWMAP_TYPE_PCF )

                        // Percentage-close filtering
                        // (9 pixel kernel)
                        // http://fabiensanglard.net/shadowmappingPCF/
                float shadow = 0.0;

        /*
                        // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
                        // must enroll loop manually

                for ( float y = -1.25; y <= 1.25; y += 1.25 )
                    for ( float x = -1.25; x <= 1.25; x += 1.25 ) {

                        vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );

                                // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup
                                //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );

                        float fDepth = unpackDepth( rgbaDepth );

                        if ( fDepth < shadowCoord.z )
                            shadow += 1.0;

                }

                shadow /= 9.0;

        */

                const float shadowDelta = 1.0 / 9.0;

                float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
                float yPixelOffset = 1.0 / shadowMapSize[ i ].y;

                float dx0 = -1.25 * xPixelOffset;
                float dy0 = -1.25 * yPixelOffset;
                float dx1 = 1.25 * xPixelOffset;
                float dy1 = 1.25 * yPixelOffset;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );
                if ( fDepth < shadowCoord.z ) shadow += shadowDelta;

                shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );

            #elif defined( SHADOWMAP_TYPE_PCF_SOFT )

                        // Percentage-close filtering
                        // (9 pixel kernel)
                        // http://fabiensanglard.net/shadowmappingPCF/
                float shadow = 0.0;

                float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
                float yPixelOffset = 1.0 / shadowMapSize[ i ].y;

                float dx0 = -1.0 * xPixelOffset;
                float dy0 = -1.0 * yPixelOffset;
                float dx1 = 1.0 * xPixelOffset;
                float dy1 = 1.0 * yPixelOffset;

                mat3 shadowKernel;
                mat3 depthKernel;

                depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
                depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
                depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
                depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
                depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
                depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
                depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
                depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
                depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );

                vec3 shadowZ = vec3( shadowCoord.z );
                shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));
                shadowKernel[0] *= vec3(0.25);

                shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));
                shadowKernel[1] *= vec3(0.25);

                shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));
                shadowKernel[2] *= vec3(0.25);

                vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );

                shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );
                shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );

                vec4 shadowValues;
                shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );
                shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );
                shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );
                shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );

                shadow = dot( shadowValues, vec4( 1.0 ) );

                shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );

            #else

                vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );
                float fDepth = unpackDepth( rgbaDepth );

                if ( fDepth < shadowCoord.z )

        // spot with multiple shadows is darker

                    shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );

        // spot with multiple shadows has the same color as single shadow spot

        //                     shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );

            #endif

        }


        #ifdef SHADOWMAP_DEBUG

            #ifdef SHADOWMAP_CASCADE

                if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];

            #else

                if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];

            #endif

        #endif

    }

    #ifdef GAMMA_OUTPUT

        shadowColor *= shadowColor;

    #endif

    gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;

#endif

#ifdef GAMMA_OUTPUT

    gl_FragColor.xyz = sqrt( gl_FragColor.xyz );

#endif
#ifdef USE_FOG

    #ifdef USE_LOGDEPTHBUF_EXT

        float depth = gl_FragDepthEXT / gl_FragCoord.w;

    #else

        float depth = gl_FragCoord.z / gl_FragCoord.w;

    #endif

    #ifdef FOG_EXP2

        const float LOG2 = 1.442695;
        float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );
        fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );

    #else

        float fogFactor = smoothstep( fogNear, fogFar, depth );

    #endif

    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );

#endif
}