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;
|