// used for replacing cc-shadow-map-base.chunk #include #if CC_SUPPORT_CASCADED_SHADOW_MAP #include #endif #include #include #pragma define SHADOWMAP_FORMAT_RGBA8 1 #pragma define SHADOWMAP_FORMAT_FLOAT 0 #pragma define CC_DIR_LIGHT_SHADOW_NONE 0 #pragma define CC_DIR_LIGHT_SHADOW_UNIFORM 1 #pragma define CC_DIR_LIGHT_SHADOW_CASCADED 2 #pragma define CC_DIR_LIGHT_SHADOW_VARIANCE 3 #pragma define CC_SHADOW_NONE 0 #pragma define CC_SHADOW_PLANAR 1 #pragma define CC_SHADOW_MAP 2 float CCGetLinearDepth(vec3 worldPos, float viewSpaceBias) { vec4 viewPos = cc_matLightView * vec4(worldPos.xyz, 1.0); viewPos.z += viewSpaceBias; return GetLinearDepthFromViewSpace(viewPos.xyz, cc_shadowNFLSInfo.x, cc_shadowNFLSInfo.y); } float CCGetLinearDepth(vec3 worldPos) { return CCGetLinearDepth(worldPos, 0.0); } #if CC_RECEIVE_SHADOW #include #include #include #include //////////////////////////////////////////////////////////Helper Functions bool GetShadowNDCPos(out vec3 shadowNDCPos, vec4 shadowPosWithDepthBias) { shadowNDCPos = shadowPosWithDepthBias.xyz / shadowPosWithDepthBias.w * 0.5 + 0.5; if (shadowNDCPos.x < 0.0 || shadowNDCPos.x > 1.0 || shadowNDCPos.y < 0.0 || shadowNDCPos.y > 1.0 || shadowNDCPos.z < 0.0 || shadowNDCPos.z > 1.0) { return false; } CC_HANDLE_NDC_SAMPLE_FLIP(shadowNDCPos.xy, cc_cameraPos.w); return true; } vec4 ApplyShadowDepthBias_FaceNormal(vec4 shadowPos, vec3 worldNormal, float normalBias, vec3 matViewDir0, vec3 matViewDir1, vec3 matViewDir2, vec2 projScaleXY) { vec4 newShadowPos = shadowPos; if (normalBias > EPSILON_LOWP) { vec3 viewNormal = vec3(dot(matViewDir0, worldNormal), dot(matViewDir1, worldNormal), dot(matViewDir2, worldNormal)); if (viewNormal.z < 0.1) newShadowPos.xy += viewNormal.xy * projScaleXY * normalBias * clamp(viewNormal.z, 0.001, 0.1); } return newShadowPos; } vec4 ApplyShadowDepthBias_FaceNormal(vec4 shadowPos, vec3 worldNormal, float normalBias, mat4 matLightView, vec2 projScaleXY) { vec4 newShadowPos = shadowPos; if (normalBias > EPSILON_LOWP) { vec4 viewNormal = matLightView * vec4(worldNormal, 0.0); if (viewNormal.z < 0.1) newShadowPos.xy += viewNormal.xy * projScaleXY * normalBias * clamp(viewNormal.z, 0.001, 0.1); } return newShadowPos; } float GetViewSpaceDepthFromNDCDepth_Orthgraphic(float NDCDepth, float projScaleZ, float projBiasZ) { return (NDCDepth - projBiasZ) / projScaleZ; } float GetViewSpaceDepthFromNDCDepth_Perspective(float NDCDepth, float homogenousDividW, float invProjScaleZ, float invProjBiasZ) { return NDCDepth * invProjScaleZ + homogenousDividW * invProjBiasZ; } vec4 ApplyShadowDepthBias_Perspective(vec4 shadowPos, float viewspaceDepthBias) { // Recover the coord in view space: cc_matLightInvProj * shadowPos vec3 viewSpacePos; viewSpacePos.xy = shadowPos.xy * cc_shadowProjInfo.zw; viewSpacePos.z = GetViewSpaceDepthFromNDCDepth_Perspective(shadowPos.z, shadowPos.w, cc_shadowInvProjDepthInfo.x, cc_shadowInvProjDepthInfo.y); // Apply bias viewSpacePos.xyz += cc_shadowProjDepthInfo.z * normalize(viewSpacePos.xyz) * viewspaceDepthBias; // Reconstuct clipspace: cc_matLightProj * viewSpacePos vec4 clipSpacePos; clipSpacePos.xy = viewSpacePos.xy * cc_shadowProjInfo.xy; clipSpacePos.zw = viewSpacePos.z * cc_shadowProjDepthInfo.xz + vec2(cc_shadowProjDepthInfo.y, 0.0); // enabled linear depth? #if CC_SHADOWMAP_USE_LINEAR_DEPTH clipSpacePos.z = GetLinearDepthFromViewSpace(viewSpacePos.xyz, cc_shadowNFLSInfo.x, cc_shadowNFLSInfo.y); clipSpacePos.z = (clipSpacePos.z * 2.0 - 1.0) * clipSpacePos.w; #endif return clipSpacePos; } // (projScaleZ, projBiasZ) = cc_shadowProjDepthInfo.xy vec4 ApplyShadowDepthBias_Orthographic(vec4 shadowPos, float viewspaceDepthBias, float projScaleZ, float projBiasZ) { float coeffA = projScaleZ; float coeffB = projBiasZ; // Recover the Z distance in view space: float viewSpacePos_z = GetViewSpaceDepthFromNDCDepth_Orthgraphic(shadowPos.z, projScaleZ, projBiasZ); // Apply bias viewSpacePos_z += viewspaceDepthBias; // Reconstuct clipspace vec4 result = shadowPos; result.z = viewSpacePos_z * coeffA + coeffB; return result; } vec4 ApplyShadowDepthBias_PerspectiveLinearDepth(vec4 shadowPos, float viewspaceDepthBias, vec3 worldPos) { // reverse operation for GetShadowNDCPos shadowPos.z = CCGetLinearDepth(worldPos, viewspaceDepthBias) * 2.0 - 1.0; shadowPos.z *= shadowPos.w; return shadowPos; } //////////////////////////////////////////////////////////Directional Light Shadow float CCGetDirLightShadowFactorHard (vec4 shadowPosWithDepthBias) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorHard(shadowNDCPos, cc_shadowMap, cc_shadowWHPBInfo.xy); } float CCGetDirLightShadowFactorSoft (vec4 shadowPosWithDepthBias) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorSoft(shadowNDCPos, cc_shadowMap, cc_shadowWHPBInfo.xy); } float CCGetDirLightShadowFactorSoft3X (vec4 shadowPosWithDepthBias) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorSoft3X(shadowNDCPos, cc_shadowMap, cc_shadowWHPBInfo.xy); } float CCGetDirLightShadowFactorSoft5X (vec4 shadowPosWithDepthBias) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorSoft5X(shadowNDCPos, cc_shadowMap, cc_shadowWHPBInfo.xy); } //////////////////////////////////////////////////////////Spot Light Shadow float CCGetSpotLightShadowFactorHard (vec4 shadowPosWithDepthBias, vec3 worldPos) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorHard(shadowNDCPos, cc_spotShadowMap, cc_shadowWHPBInfo.xy); } float CCGetSpotLightShadowFactorSoft (vec4 shadowPosWithDepthBias, vec3 worldPos) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorSoft(shadowNDCPos, cc_spotShadowMap, cc_shadowWHPBInfo.xy); } float CCGetSpotLightShadowFactorSoft3X (vec4 shadowPosWithDepthBias, vec3 worldPos) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorSoft3X(shadowNDCPos, cc_spotShadowMap, cc_shadowWHPBInfo.xy); } float CCGetSpotLightShadowFactorSoft5X (vec4 shadowPosWithDepthBias, vec3 worldPos) { vec3 shadowNDCPos; if (!GetShadowNDCPos(shadowNDCPos, shadowPosWithDepthBias)) { return 1.0; } return NativePCFShadowFactorSoft5X(shadowNDCPos, cc_spotShadowMap, cc_shadowWHPBInfo.xy); } //////////////////////////////////////////////////////////Main Functions float CCSpotShadowFactorBase(out vec4 shadowPosWithDepthBias, vec4 shadowPos, vec3 worldPos, vec2 shadowBias) { float pcf = cc_shadowWHPBInfo.z; vec4 pos = vec4(1.0); #if CC_SHADOWMAP_USE_LINEAR_DEPTH pos = ApplyShadowDepthBias_PerspectiveLinearDepth(shadowPos, shadowBias.x, worldPos); #else pos = ApplyShadowDepthBias_Perspective(shadowPos, shadowBias.x); #endif float realtimeShadow = 1.0; if (pcf > 2.9) { realtimeShadow = CCGetSpotLightShadowFactorSoft5X(pos, worldPos); }else if (pcf > 1.9) { realtimeShadow = CCGetSpotLightShadowFactorSoft3X(pos, worldPos); }else if (pcf > 0.9) { realtimeShadow = CCGetSpotLightShadowFactorSoft(pos, worldPos); }else { realtimeShadow = CCGetSpotLightShadowFactorHard(pos, worldPos); } shadowPosWithDepthBias = pos; return mix(realtimeShadow, 1.0, cc_shadowNFLSInfo.w); } float CCShadowFactorBase(out vec4 shadowPosWithDepthBias, vec4 shadowPos, vec3 N, vec2 shadowBias) { vec4 pos = ApplyShadowDepthBias_FaceNormal(shadowPos, N, shadowBias.y, cc_matLightView, cc_shadowProjInfo.xy); pos = ApplyShadowDepthBias_Orthographic(pos, shadowBias.x, cc_shadowProjDepthInfo.x, cc_shadowProjDepthInfo.y); float realtimeShadow = 1.0; #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT_5X realtimeShadow = CCGetDirLightShadowFactorSoft5X(pos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT_3X realtimeShadow = CCGetDirLightShadowFactorSoft3X(pos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT realtimeShadow = CCGetDirLightShadowFactorSoft(pos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_HARD realtimeShadow = CCGetDirLightShadowFactorHard(pos); #endif shadowPosWithDepthBias = pos; return mix(realtimeShadow, 1.0, cc_shadowNFLSInfo.w); } #if CC_SUPPORT_CASCADED_SHADOW_MAP bool CCGetCSMLevelWithTransition(out highp float ratio, vec3 clipPos) { highp float maxRange = 1.0 - cc_csmSplitsInfo.x; highp float minRange = cc_csmSplitsInfo.x; highp float thresholdInvert = 1.0 / cc_csmSplitsInfo.x; ratio = 0.0; if (clipPos.x <= minRange) { ratio = clipPos.x * thresholdInvert; return true; } if (clipPos.x >= maxRange) { ratio = 1.0 - (clipPos.x - maxRange) * thresholdInvert; return true; } if (clipPos.y <= minRange) { ratio = clipPos.y * thresholdInvert; return true; } if (clipPos.y >= maxRange) { ratio = 1.0 - (clipPos.y - maxRange) * thresholdInvert; return true; } return false; } bool CCHasCSMLevel(int level, vec3 worldPos) { highp float layerThreshold = cc_csmViewDir0[0].w; bool hasLevel = false; for (int i = 0; i < NUMCASCADES; i++) { if (i == level) { vec4 shadowPos = cc_matCSMViewProj[i] * vec4(worldPos.xyz, 1.0); vec3 clipPos = shadowPos.xyz / shadowPos.w * 0.5 + 0.5; if (clipPos.x >= layerThreshold && clipPos.x <= (1.0 - layerThreshold) && clipPos.y >= layerThreshold && clipPos.y <= (1.0 - layerThreshold) && clipPos.z >= 0.0 && clipPos.z <= 1.0) { hasLevel = true; } } } return hasLevel; } void CCGetCSMLevel(out vec4 csmPos, out vec4 shadowProjDepthInfo, out vec4 shadowProjInfo, out vec3 shadowViewDir0, out vec3 shadowViewDir1, out vec3 shadowViewDir2, vec3 worldPos, int level) { highp float layerThreshold = cc_csmViewDir0[0].w; for (int i = 0; i < NUMCASCADES; i++) { vec4 shadowPos = cc_matCSMViewProj[i] * vec4(worldPos.xyz, 1.0); vec3 clipPos = shadowPos.xyz / shadowPos.w * 0.5 + 0.5; if (clipPos.x >= layerThreshold && clipPos.x <= (1.0 - layerThreshold) && clipPos.y >= layerThreshold && clipPos.y <= (1.0 - layerThreshold) && clipPos.z >= 0.0 && clipPos.z <= 1.0 && i == level) { csmPos = cc_matCSMViewProj[i] * vec4(worldPos.xyz, 1.0); csmPos.xy = csmPos.xy * cc_csmAtlas[i].xy + cc_csmAtlas[i].zw; shadowProjDepthInfo = cc_csmProjDepthInfo[i]; shadowProjInfo = cc_csmProjInfo[i]; shadowViewDir0 = cc_csmViewDir0[i].xyz; shadowViewDir1 = cc_csmViewDir1[i].xyz; shadowViewDir2 = cc_csmViewDir2[i].xyz; } } } int CCGetCSMLevel(out bool isTransitionArea, out highp float transitionRatio, out vec4 csmPos, out vec4 shadowProjDepthInfo, out vec4 shadowProjInfo, out vec3 shadowViewDir0, out vec3 shadowViewDir1, out vec3 shadowViewDir2, vec3 worldPos) { int level = -1; highp float layerThreshold = cc_csmViewDir0[0].w; for (int i = 0; i < NUMCASCADES; i++) { vec4 shadowPos = cc_matCSMViewProj[i] * vec4(worldPos.xyz, 1.0); vec3 clipPos = shadowPos.xyz / shadowPos.w * 0.5 + 0.5; if (clipPos.x >= layerThreshold && clipPos.x <= (1.0 - layerThreshold) && clipPos.y >= layerThreshold && clipPos.y <= (1.0 - layerThreshold) && clipPos.z >= 0.0 && clipPos.z <= 1.0 && level < 0) { #if CC_CASCADED_LAYERS_TRANSITION isTransitionArea = CCGetCSMLevelWithTransition(transitionRatio, clipPos); #endif csmPos = cc_matCSMViewProj[i] * vec4(worldPos.xyz, 1.0); csmPos.xy = csmPos.xy * cc_csmAtlas[i].xy + cc_csmAtlas[i].zw; shadowProjDepthInfo = cc_csmProjDepthInfo[i]; shadowProjInfo = cc_csmProjInfo[i]; shadowViewDir0 = cc_csmViewDir0[i].xyz; shadowViewDir1 = cc_csmViewDir1[i].xyz; shadowViewDir2 = cc_csmViewDir2[i].xyz; level = i; } } return level; } int CCGetCSMLevel(out vec4 csmPos, out vec4 shadowProjDepthInfo, out vec4 shadowProjInfo, out vec3 shadowViewDir0, out vec3 shadowViewDir1, out vec3 shadowViewDir2, vec3 worldPos) { bool isTransitionArea = false; highp float transitionRatio = 0.0; return CCGetCSMLevel(isTransitionArea, transitionRatio, csmPos, shadowProjDepthInfo, shadowProjInfo, shadowViewDir0, shadowViewDir1, shadowViewDir2, worldPos); } // output csmPos is non-biased position that can be used for sampling shadow map after homogeneous divid float CCCSMFactorBase(out vec4 csmPos, out vec4 csmPosWithBias, vec3 worldPos, vec3 N, vec2 shadowBias) { bool isTransitionArea = false; highp float ratio = 0.0; csmPos = vec4(1.0); vec4 shadowProjDepthInfo, shadowProjInfo; vec3 shadowViewDir0, shadowViewDir1, shadowViewDir2; int level = -1; #if CC_CASCADED_LAYERS_TRANSITION level = CCGetCSMLevel(isTransitionArea, ratio, csmPos, shadowProjDepthInfo, shadowProjInfo, shadowViewDir0, shadowViewDir1, shadowViewDir2, worldPos); #else level = CCGetCSMLevel(csmPos, shadowProjDepthInfo, shadowProjInfo, shadowViewDir0, shadowViewDir1, shadowViewDir2, worldPos); #endif if (level < 0) { return 1.0; } vec4 pos = ApplyShadowDepthBias_FaceNormal(csmPos, N, shadowBias.y, shadowViewDir0, shadowViewDir1, shadowViewDir2, shadowProjInfo.xy); pos = ApplyShadowDepthBias_Orthographic(pos, shadowBias.x, shadowProjDepthInfo.x, shadowProjDepthInfo.y); csmPosWithBias = pos; float realtimeShadow = 1.0; #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT_5X realtimeShadow = CCGetDirLightShadowFactorSoft5X(pos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT_3X realtimeShadow = CCGetDirLightShadowFactorSoft3X(pos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT realtimeShadow = CCGetDirLightShadowFactorSoft(pos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_HARD realtimeShadow = CCGetDirLightShadowFactorHard(pos); #endif #if CC_CASCADED_LAYERS_TRANSITION vec4 nextCSMPos = vec4(1.0); vec4 nextShadowProjDepthInfo, nextShadowProjInfo; vec3 nextShadowViewDir0, nextShadowViewDir1, nextShadowViewDir2; float nextRealtimeShadow = 1.0; CCGetCSMLevel(nextCSMPos, nextShadowProjDepthInfo, nextShadowProjInfo, nextShadowViewDir0, nextShadowViewDir1, nextShadowViewDir2, worldPos, level + 1); bool hasNextLevel = CCHasCSMLevel(level + 1, worldPos); if (hasNextLevel && isTransitionArea) { vec4 nexPos = ApplyShadowDepthBias_FaceNormal(nextCSMPos, N, shadowBias.y, nextShadowViewDir0, nextShadowViewDir1, nextShadowViewDir2, nextShadowProjInfo.xy); nexPos = ApplyShadowDepthBias_Orthographic(nexPos, shadowBias.x, nextShadowProjDepthInfo.x, nextShadowProjDepthInfo.y); #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT_5X nextRealtimeShadow = CCGetDirLightShadowFactorSoft5X(nexPos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT_3X nextRealtimeShadow = CCGetDirLightShadowFactorSoft3X(nexPos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_SOFT nextRealtimeShadow = CCGetDirLightShadowFactorSoft(nexPos); #endif #if CC_DIR_SHADOW_PCF_TYPE == CC_SHADOW_PCF_HARD nextRealtimeShadow = CCGetDirLightShadowFactorHard(nexPos); #endif return mix(mix(nextRealtimeShadow, realtimeShadow, ratio), 1.0, cc_shadowNFLSInfo.w); } return mix(realtimeShadow, 1.0, cc_shadowNFLSInfo.w); #else return mix(realtimeShadow, 1.0, cc_shadowNFLSInfo.w); #endif } #else int CCGetCSMLevel(out vec4 csmPos, out vec4 shadowProjDepthInfo, out vec4 shadowProjInfo, out vec3 shadowViewDir0, out vec3 shadowViewDir1, out vec3 shadowViewDir2, vec3 worldPos) { return -1; } float CCCSMFactorBase(out vec4 csmPos, out vec4 csmPosWithBias, vec3 worldPos, vec3 N, vec2 shadowBias) { csmPos = cc_matLightViewProj * vec4(worldPos, 1.0); return CCShadowFactorBase(csmPosWithBias, csmPos, N, shadowBias); } #endif // compatible version float CCShadowFactorBase(vec4 shadowPos, vec3 N, vec2 shadowBias) { vec4 shadowPosWithDepthBias; return CCShadowFactorBase(shadowPosWithDepthBias, shadowPos, N, shadowBias); } float CCCSMFactorBase(vec3 worldPos, vec3 N, vec2 shadowBias) { vec4 csmPos, csmPosWithBias; return CCCSMFactorBase(csmPos, csmPosWithBias, worldPos, N, shadowBias); } float CCSpotShadowFactorBase(vec4 shadowPos, vec3 worldPos, vec2 shadowBias) { vec4 shadowPosWithDepthBias; return CCSpotShadowFactorBase(shadowPosWithDepthBias, shadowPos, worldPos, shadowBias); } #endif