| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 | import defined from "../Core/defined.js";import ShaderSource from "../Renderer/ShaderSource.js";/** * @private */function ShadowMapShader() {}ShadowMapShader.getShadowCastShaderKeyword = function (  isPointLight,  isTerrain,  usesDepthTexture,  isOpaque) {  return `castShadow ${isPointLight} ${isTerrain} ${usesDepthTexture} ${isOpaque}`;};ShadowMapShader.createShadowCastVertexShader = function (  vs,  isPointLight,  isTerrain) {  const defines = vs.defines.slice(0);  const sources = vs.sources.slice(0);  defines.push("SHADOW_MAP");  if (isTerrain) {    defines.push("GENERATE_POSITION");  }  const positionVaryingName = ShaderSource.findPositionVarying(vs);  const hasPositionVarying = defined(positionVaryingName);  if (isPointLight && !hasPositionVarying) {    const length = sources.length;    for (let j = 0; j < length; ++j) {      sources[j] = ShaderSource.replaceMain(sources[j], "czm_shadow_cast_main");    }    const shadowVS =      "varying vec3 v_positionEC; \n" +      "void main() \n" +      "{ \n" +      "    czm_shadow_cast_main(); \n" +      "    v_positionEC = (czm_inverseProjection * gl_Position).xyz; \n" +      "}";    sources.push(shadowVS);  }  return new ShaderSource({    defines: defines,    sources: sources,  });};ShadowMapShader.createShadowCastFragmentShader = function (  fs,  isPointLight,  usesDepthTexture,  opaque) {  const defines = fs.defines.slice(0);  const sources = fs.sources.slice(0);  defines.push("SHADOW_MAP");  let positionVaryingName = ShaderSource.findPositionVarying(fs);  const hasPositionVarying = defined(positionVaryingName);  if (!hasPositionVarying) {    positionVaryingName = "v_positionEC";  }  const length = sources.length;  for (let i = 0; i < length; ++i) {    sources[i] = ShaderSource.replaceMain(sources[i], "czm_shadow_cast_main");  }  let fsSource = "";  if (isPointLight) {    if (!hasPositionVarying) {      fsSource += "varying vec3 v_positionEC; \n";    }    fsSource += "uniform vec4 shadowMap_lightPositionEC; \n";  }  if (opaque) {    fsSource += "void main() \n" + "{ \n";  } else {    fsSource +=      "void main() \n" +      "{ \n" +      "    czm_shadow_cast_main(); \n" +      "    if (gl_FragColor.a == 0.0) \n" +      "    { \n" +      "       discard; \n" +      "    } \n";  }  if (isPointLight) {    fsSource +=      `    float distance = length(${positionVaryingName}); \n` +      `    if (distance >= shadowMap_lightPositionEC.w) \n` +      `    { \n` +      `        discard; \n` +      `    } \n` +      `    distance /= shadowMap_lightPositionEC.w; // radius \n` +      `    gl_FragColor = czm_packDepth(distance); \n`;  } else if (usesDepthTexture) {    fsSource += "    gl_FragColor = vec4(1.0); \n";  } else {    fsSource += "    gl_FragColor = czm_packDepth(gl_FragCoord.z); \n";  }  fsSource += "} \n";  sources.push(fsSource);  return new ShaderSource({    defines: defines,    sources: sources,  });};ShadowMapShader.getShadowReceiveShaderKeyword = function (  shadowMap,  castShadows,  isTerrain,  hasTerrainNormal) {  const usesDepthTexture = shadowMap._usesDepthTexture;  const polygonOffsetSupported = shadowMap._polygonOffsetSupported;  const isPointLight = shadowMap._isPointLight;  const isSpotLight = shadowMap._isSpotLight;  const hasCascades = shadowMap._numberOfCascades > 1;  const debugCascadeColors = shadowMap.debugCascadeColors;  const softShadows = shadowMap.softShadows;  return `receiveShadow ${usesDepthTexture}${polygonOffsetSupported}${isPointLight}${isSpotLight}${hasCascades}${debugCascadeColors}${softShadows}${castShadows}${isTerrain}${hasTerrainNormal}`;};ShadowMapShader.createShadowReceiveVertexShader = function (  vs,  isTerrain,  hasTerrainNormal) {  const defines = vs.defines.slice(0);  const sources = vs.sources.slice(0);  defines.push("SHADOW_MAP");  if (isTerrain) {    if (hasTerrainNormal) {      defines.push("GENERATE_POSITION_AND_NORMAL");    } else {      defines.push("GENERATE_POSITION");    }  }  return new ShaderSource({    defines: defines,    sources: sources,  });};ShadowMapShader.createShadowReceiveFragmentShader = function (  fs,  shadowMap,  castShadows,  isTerrain,  hasTerrainNormal) {  const normalVaryingName = ShaderSource.findNormalVarying(fs);  const hasNormalVarying =    (!isTerrain && defined(normalVaryingName)) ||    (isTerrain && hasTerrainNormal);  const positionVaryingName = ShaderSource.findPositionVarying(fs);  const hasPositionVarying = defined(positionVaryingName);  const usesDepthTexture = shadowMap._usesDepthTexture;  const polygonOffsetSupported = shadowMap._polygonOffsetSupported;  const isPointLight = shadowMap._isPointLight;  const isSpotLight = shadowMap._isSpotLight;  const hasCascades = shadowMap._numberOfCascades > 1;  const debugCascadeColors = shadowMap.debugCascadeColors;  const softShadows = shadowMap.softShadows;  const bias = isPointLight    ? shadowMap._pointBias    : isTerrain    ? shadowMap._terrainBias    : shadowMap._primitiveBias;  const defines = fs.defines.slice(0);  const sources = fs.sources.slice(0);  const length = sources.length;  for (let i = 0; i < length; ++i) {    sources[i] = ShaderSource.replaceMain(      sources[i],      "czm_shadow_receive_main"    );  }  if (isPointLight) {    defines.push("USE_CUBE_MAP_SHADOW");  } else if (usesDepthTexture) {    defines.push("USE_SHADOW_DEPTH_TEXTURE");  }  if (softShadows && !isPointLight) {    defines.push("USE_SOFT_SHADOWS");  }  // Enable day-night shading so that the globe is dark when the light is below the horizon  if (hasCascades && castShadows && isTerrain) {    if (hasNormalVarying) {      defines.push("ENABLE_VERTEX_LIGHTING");    } else {      defines.push("ENABLE_DAYNIGHT_SHADING");    }  }  if (castShadows && bias.normalShading && hasNormalVarying) {    defines.push("USE_NORMAL_SHADING");    if (bias.normalShadingSmooth > 0.0) {      defines.push("USE_NORMAL_SHADING_SMOOTH");    }  }  let fsSource = "";  if (isPointLight) {    fsSource += "uniform samplerCube shadowMap_textureCube; \n";  } else {    fsSource += "uniform sampler2D shadowMap_texture; \n";  }  let returnPositionEC;  if (hasPositionVarying) {    returnPositionEC = `    return vec4(${positionVaryingName}, 1.0); \n`;  } else {    returnPositionEC =      "#ifndef LOG_DEPTH \n" +      "    return czm_windowToEyeCoordinates(gl_FragCoord); \n" +      "#else \n" +      "    return vec4(v_logPositionEC, 1.0); \n" +      "#endif \n";  }  fsSource +=    `${      "uniform mat4 shadowMap_matrix; \n" +      "uniform vec3 shadowMap_lightDirectionEC; \n" +      "uniform vec4 shadowMap_lightPositionEC; \n" +      "uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness; \n" +      "uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth; \n" +      "#ifdef LOG_DEPTH \n" +      "varying vec3 v_logPositionEC; \n" +      "#endif \n" +      "vec4 getPositionEC() \n" +      "{ \n"    }${returnPositionEC}} \n` +    `vec3 getNormalEC() \n` +    `{ \n${      hasNormalVarying        ? `    return normalize(${normalVaryingName}); \n`        : "    return vec3(1.0); \n"    }} \n` +    // Offset the shadow position in the direction of the normal for perpendicular and back faces    `void applyNormalOffset(inout vec4 positionEC, vec3 normalEC, float nDotL) \n` +    `{ \n${      bias.normalOffset && hasNormalVarying        ? "    float normalOffset = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.x; \n" +          "    float normalOffsetScale = 1.0 - nDotL; \n" +          "    vec3 offset = normalOffset * normalOffsetScale * normalEC; \n" +          "    positionEC.xyz += offset; \n"        : ""    }} \n`;  fsSource +=    "void main() \n" +    "{ \n" +    "    czm_shadow_receive_main(); \n" +    "    vec4 positionEC = getPositionEC(); \n" +    "    vec3 normalEC = getNormalEC(); \n" +    "    float depth = -positionEC.z; \n";  fsSource +=    "    czm_shadowParameters shadowParameters; \n" +    "    shadowParameters.texelStepSize = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy; \n" +    "    shadowParameters.depthBias = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z; \n" +    "    shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w; \n" +    "    shadowParameters.darkness = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w; \n";  if (isTerrain) {    // Scale depth bias based on view distance to reduce z-fighting in distant terrain    fsSource += "    shadowParameters.depthBias *= max(depth * 0.01, 1.0); \n";  } else if (!polygonOffsetSupported) {    // If polygon offset isn't supported push the depth back based on view, however this    // causes light leaking at further away views    fsSource +=      "    shadowParameters.depthBias *= mix(1.0, 100.0, depth * 0.0015); \n";  }  if (isPointLight) {    fsSource +=      "    vec3 directionEC = positionEC.xyz - shadowMap_lightPositionEC.xyz; \n" +      "    float distance = length(directionEC); \n" +      "    directionEC = normalize(directionEC); \n" +      "    float radius = shadowMap_lightPositionEC.w; \n" +      "    // Stop early if the fragment is beyond the point light radius \n" +      "    if (distance > radius) \n" +      "    { \n" +      "        return; \n" +      "    } \n" +      "    vec3 directionWC  = czm_inverseViewRotation * directionEC; \n" +      "    shadowParameters.depth = distance / radius; \n" +      "    shadowParameters.nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0); \n" +      "    shadowParameters.texCoords = directionWC; \n" +      "    float visibility = czm_shadowVisibility(shadowMap_textureCube, shadowParameters); \n";  } else if (isSpotLight) {    fsSource +=      "    vec3 directionEC = normalize(positionEC.xyz - shadowMap_lightPositionEC.xyz); \n" +      "    float nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0); \n" +      "    applyNormalOffset(positionEC, normalEC, nDotL); \n" +      "    vec4 shadowPosition = shadowMap_matrix * positionEC; \n" +      "    // Spot light uses a perspective projection, so perform the perspective divide \n" +      "    shadowPosition /= shadowPosition.w; \n" +      "    // Stop early if the fragment is not in the shadow bounds \n" +      "    if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) \n" +      "    { \n" +      "        return; \n" +      "    } \n" +      "    shadowParameters.texCoords = shadowPosition.xy; \n" +      "    shadowParameters.depth = shadowPosition.z; \n" +      "    shadowParameters.nDotL = nDotL; \n" +      "    float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n";  } else if (hasCascades) {    fsSource += `${      "    float maxDepth = shadowMap_cascadeSplits[1].w; \n" +      "    // Stop early if the eye depth exceeds the last cascade \n" +      "    if (depth > maxDepth) \n" +      "    { \n" +      "        return; \n" +      "    } \n" +      "    // Get the cascade based on the eye-space depth \n" +      "    vec4 weights = czm_cascadeWeights(depth); \n" +      "    // Apply normal offset \n" +      "    float nDotL = clamp(dot(normalEC, shadowMap_lightDirectionEC), 0.0, 1.0); \n" +      "    applyNormalOffset(positionEC, normalEC, nDotL); \n" +      "    // Transform position into the cascade \n" +      "    vec4 shadowPosition = czm_cascadeMatrix(weights) * positionEC; \n" +      "    // Get visibility \n" +      "    shadowParameters.texCoords = shadowPosition.xy; \n" +      "    shadowParameters.depth = shadowPosition.z; \n" +      "    shadowParameters.nDotL = nDotL; \n" +      "    float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n" +      "    // Fade out shadows that are far away \n" +      "    float shadowMapMaximumDistance = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.z; \n" +      "    float fade = max((depth - shadowMapMaximumDistance * 0.8) / (shadowMapMaximumDistance * 0.2), 0.0); \n" +      "    visibility = mix(visibility, 1.0, fade); \n"    }${      debugCascadeColors        ? "    // Draw cascade colors for debugging \n" +          "    gl_FragColor *= czm_cascadeColor(weights); \n"        : ""    }`;  } else {    fsSource +=      "    float nDotL = clamp(dot(normalEC, shadowMap_lightDirectionEC), 0.0, 1.0); \n" +      "    applyNormalOffset(positionEC, normalEC, nDotL); \n" +      "    vec4 shadowPosition = shadowMap_matrix * positionEC; \n" +      "    // Stop early if the fragment is not in the shadow bounds \n" +      "    if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) \n" +      "    { \n" +      "        return; \n" +      "    } \n" +      "    shadowParameters.texCoords = shadowPosition.xy; \n" +      "    shadowParameters.depth = shadowPosition.z; \n" +      "    shadowParameters.nDotL = nDotL; \n" +      "    float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n";  }  fsSource += "    gl_FragColor.rgb *= visibility; \n" + "} \n";  sources.push(fsSource);  return new ShaderSource({    defines: defines,    sources: sources,  });};export default ShadowMapShader;
 |