Add existing to tracked
This commit is contained in:
+443
@@ -0,0 +1,443 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import ShaderSource from "../Renderer/ShaderSource.js";
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function ShadowMapShader() {}
|
||||
|
||||
const clippingDefines = {
|
||||
HAS_CLIPPING_PLANES: true,
|
||||
ENABLE_CLIPPING_POLYGONS: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the fragment shader clips fragments via a
|
||||
* ClippingPlaneCollection or ClippingPolygonCollection, in which case the
|
||||
* shadow cast pass must run the original main so that clipped fragments
|
||||
* are discarded and do not cast shadows.
|
||||
*
|
||||
* @param {ShaderSource} fragmentShaderSource
|
||||
* @returns {boolean}
|
||||
*/
|
||||
ShadowMapShader.hasClippingForShadowCast = function (fragmentShaderSource) {
|
||||
const defines = fragmentShaderSource.defines;
|
||||
return (
|
||||
defined(defines) &&
|
||||
defines.some(function (define) {
|
||||
return clippingDefines[define] === true;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean} isPointLight
|
||||
* @param {boolean} isTerrain
|
||||
* @param {boolean} usesDepthTexture
|
||||
* @param {boolean} isOpaque
|
||||
* @param {boolean} hasClipping True if the fragment shader discards clipped
|
||||
* fragments. Differentiates cached shader variants so that a shader that
|
||||
* must call czm_shadow_cast_main() is never reused for one that does not.
|
||||
* @returns {string}
|
||||
*/
|
||||
ShadowMapShader.getShadowCastShaderKeyword = function (
|
||||
isPointLight,
|
||||
isTerrain,
|
||||
usesDepthTexture,
|
||||
isOpaque,
|
||||
hasClipping,
|
||||
) {
|
||||
return `castShadow ${isPointLight} ${isTerrain} ${usesDepthTexture} ${isOpaque} ${hasClipping}`;
|
||||
};
|
||||
|
||||
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 =
|
||||
"out 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,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ShaderSource} fs
|
||||
* @param {boolean} isPointLight
|
||||
* @param {boolean} usesDepthTexture
|
||||
* @param {boolean} opaque
|
||||
* @param {boolean} hasClipping True if the fragment shader discards clipped
|
||||
* fragments. When true, czm_shadow_cast_main() (the renamed original main)
|
||||
* is called first so that clipping discards execute during the shadow cast pass.
|
||||
* @returns {ShaderSource}
|
||||
*/
|
||||
ShadowMapShader.createShadowCastFragmentShader = function (
|
||||
fs,
|
||||
isPointLight,
|
||||
usesDepthTexture,
|
||||
opaque,
|
||||
hasClipping,
|
||||
) {
|
||||
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 += "in vec3 v_positionEC; \n";
|
||||
}
|
||||
fsSource += "uniform vec4 shadowMap_lightPositionEC; \n";
|
||||
}
|
||||
|
||||
if (opaque && hasClipping) {
|
||||
fsSource += "void main() \n" + "{ \n" + " czm_shadow_cast_main(); \n";
|
||||
} else if (opaque) {
|
||||
fsSource += "void main() \n" + "{ \n";
|
||||
} else {
|
||||
fsSource +=
|
||||
"void main() \n" +
|
||||
"{ \n" +
|
||||
" czm_shadow_cast_main(); \n" +
|
||||
" if (out_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` +
|
||||
` out_FragColor = czm_packDepth(distance); \n`;
|
||||
} else if (usesDepthTexture) {
|
||||
fsSource += " out_FragColor = vec4(1.0); \n";
|
||||
} else {
|
||||
fsSource += " out_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" +
|
||||
"in 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" +
|
||||
" out_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 += " out_FragColor.rgb *= visibility; \n" + "} \n";
|
||||
|
||||
sources.push(fsSource);
|
||||
|
||||
return new ShaderSource({
|
||||
defines: defines,
|
||||
sources: sources,
|
||||
});
|
||||
};
|
||||
export default ShadowMapShader;
|
||||
Reference in New Issue
Block a user