ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

屏幕空间接触阴影(Contact Shadows):利用深度缓冲光线步进补全微小缝隙阴影

屏幕空间接触阴影(Contact Shadows):利用深度缓冲光线步进补全微小缝隙阴影 在基于物理的大型开放世界渲染中级联阴影贴图Cascaded Shadow Maps, CSM是处理大范围日光阴影的工业标准。然而受限于有限的阴影贴图分辨率通常为 2048x2048 或 4096x4096以及为根除阴影粉刺Acne而不得不引入的法线与深度偏置Normal Offset Depth BiasCSM 在处理微观尺度细节时存在天然的几何短板。最典型的现象便是角色脚底与地面交界处的“漂浮悬空Peter Panning”、下颌与衣领处的无阴影漏光以及细小植被、碎石缝隙处阴影的严重缺失极大削弱了画面的物理接地感Grounding。屏幕空间接触阴影Screen Space Contact Shadows是弥补这一缺陷成本最低且效果最显著的技术方案。它不占用额外的阴影贴图显存直接利用 G-Buffer 阶段已经生成的高分辨率屏幕深度缓冲在片元着色器中沿着指向光源的方向进行短距离屏幕空间光线步进Ray Marching以极高的几何保真度精准补全厘米级的微观缝隙接触阴影。算法拓扑与屏幕空间光线步进几何接触阴影只负责填补从物体表面向外延伸的微小距离通常在世界空间 0.1 米至 0.5 米以内更远处的阴影则平滑交由 CSM 接管。其核心计算流程如下起点反求从屏幕 UV 提取深度值通过逆变换还原当前着色像素的视空间位置 $\mathbf{P}_{view}$。光线方向构建将世界空间阳光方向向量变换至视空间得到步进向量 $\mathbf{L}_{view}$。蓝噪声扰动Blue Noise Jitter为了避免固定步长采样引发的阶梯形采样带伪影Banding Artifacts使用交错网格或蓝噪声纹理对光线起点施加微小的随机相位偏移。步进与厚度深度测试Depth Thickness Test在视空间或 NDC 投影空间沿光线迭代步进在每一步将光线采样点投影到屏幕 UV 坐标采集该处的实际表面深度判断光线是否被其他几何体遮挡。Light Direction L ▲ / / Ray Marching Steps (8~16 步) / * * * * (投影回屏幕采样深度缓冲) / P (当前像素视空间坐标) ────┴────────────── 地面微小缝隙接触阴影核心片元着色器实现以下是在 Vulkan/GLSL 环境下实现高保真接触阴影的片元着色器核心函数// ContactShadows.glsl - 屏幕空间接触阴影步进与厚度判定 #version 450 layout(binding 0) uniform sampler2D uDepthTexture; layout(binding 1) uniform sampler2D uBlueNoise; layout(push_constant) uniform ContactShadowUBO { mat4 projMatrix; // 透视投影矩阵 mat4 invProjMatrix; // 逆投影矩阵 vec3 lightDirView; // 视空间中的光线方向 (归一化) float maxRayLength; // 最大世界/视空间步进长度 (如 0.25米) int maxSteps; // 最大步进步数 (推荐 12 ~ 16 步) float thickness; // 判定遮挡的物体厚度阈值 (如 0.05米) } ubo; // 从屏幕 UV 与深度缓冲还原视空间坐标 vec3 GetViewPosition(vec2 uv, float depth) { vec4 clipPos vec4(uv * 2.0 - 1.0, depth, 1.0); vec4 viewPos ubo.invProjMatrix * clipPos; return viewPos.xyz / viewPos.w; } // 屏幕空间接触阴影核心步进 float CalculateContactShadow(vec2 screenUV, vec3 viewPos, vec2 screenResolution) { // 1. 采集蓝噪声抖动因子 (0.0 ~ 1.0) vec2 noiseUV screenResolution / vec2(textureSize(uBlueNoise, 0)) * screenUV; float jitter texture(uBlueNoise, noiseUV).r; // 2. 初始化光线步进参数 vec3 rayDir ubo.lightDirView; float stepSize ubo.maxRayLength / float(ubo.maxSteps); // 应用起点抖动打破规则条带 vec3 currentPos viewPos rayDir * (stepSize * jitter); float shadowFactor 1.0; // 1.0 为完全照亮0.0 为完全阴影 for (int i 0; i ubo.maxSteps; i) { currentPos rayDir * stepSize; // 3. 将视空间点投影回 NDC 并映射至屏幕 UV vec4 projPos ubo.projMatrix * vec4(currentPos, 1.0); projPos.xyz / projPos.w; vec2 sampleUV projPos.xy * 0.5 0.5; // 边界保护若光线步出屏幕视口立即终止判定 if (sampleUV.x 0.0 || sampleUV.x 1.0 || sampleUV.y 0.0 || sampleUV.y 1.0) { break; } // 4. 读取采样点处的实际几何体深度 float sampledDepth texture(uDepthTexture, sampleUV).r; if (sampledDepth 1.0) continue; // 击中天空盒 vec3 sampledViewPos GetViewPosition(sampleUV, sampledDepth); // 5. 深度对比当前光线深度是否在物体表面之后且未穿透厚度阈值 // 在视空间中Z 轴通常为负距离摄像机越远 Z 越小 float depthDiff sampledViewPos.z - currentPos.z; if (depthDiff 0.0 depthDiff ubo.thickness) { // 发生微观遮挡计算基于步进距离的平滑淡出 float fade 1.0 - (float(i) / float(ubo.maxSteps)); shadowFactor 1.0 - fade; break; } } return shadowFactor; }屏幕空间局限性与 CSM 的平滑融合由于屏幕空间技术只能采集当前视口已经光栅化出的几何信息当遮挡体位于摄像机视锥体之外、或者被其他前景物体完全遮挡时接触阴影便无法检出容易出现阴影突然消失的瑕疵。必须设计严密的融合与兜底逻辑视口边缘衰减Screen Edge Fade当光线采样的sampleUV逼近屏幕四周边缘如距离边缘小于 5% 像素区时根据边缘距离线性衰减接触阴影强度杜绝摄像机旋转时光线突然被截断引起的突变闪烁。乘法复合与极值选取在计算最终直接光照可见性时将接触阴影因子 $S_{contact}$ 与大尺度级联阴影因子 $S_{CSM}$ 取最小值$\min(S_{CSM}, S_{contact})$进行压制float finalShadow min(csmShadow, contactShadow);在近距离缝隙处接触阴影能瞬间“拉回”因 CSM 偏置而悬浮的阴影在远距离处接触阴影平滑衰减为 1.0完全由 CSM 接管大范围树影和山影。性能实测与开销控制屏幕空间接触阴影的性能完全由分辨率与采样步数决定。半分辨率执行Half-Resolution Pass在高端桌面平台可以在全分辨率全屏 Quad 中直接执行 12 步步进耗时约 0.4ms1440p。空间降采样与双边滤波Bilateral Upsample在移动端或主机平台上将接触阴影单独提取为一个半分辨率 Compute Pass步数降为 8 步利用深度感知的双边联合上采样Joint Bilateral Upsample贴回全分辨率光照缓冲整体开销可直接压降至 0.12ms 左右。微小缝隙阴影的回归为角色脚底与细密场景赋予了坚实可靠的物理真实感。
返回列表