SRP 实现阴影(适用于平行光)

世界空间到阴影贴图空间

  • 概述:用Shadow Map的方式实现硬/软阴影,及一些过程中缺陷优化的实现。

参考:directional-shadows
Catlike Coding的自定义渲染管线系列教程中给出了下列代码。但一开始我是没看明白的,所以推了一下,思路就清晰多了。

点击查看代码
float scale = 1f / split;
m.m00 = (0.5f * (m.m00 + m.m30) + offset.x * m.m30) * scale;
m.m01 = (0.5f * (m.m01 + m.m31) + offset.x * m.m31) * scale;
m.m02 = (0.5f * (m.m02 + m.m32) + offset.x * m.m32) * scale;
m.m03 = (0.5f * (m.m03 + m.m33) + offset.x * m.m33) * scale;
m.m10 = (0.5f * (m.m10 + m.m30) + offset.y * m.m30) * scale;
m.m11 = (0.5f * (m.m11 + m.m31) + offset.y * m.m31) * scale;
m.m12 = (0.5f * (m.m12 + m.m32) + offset.y * m.m32) * scale;
m.m13 = (0.5f * (m.m13 + m.m33) + offset.y * m.m33) * scale;

直接根据原理采样后的效果

🤧用了shadowSpace.z + 0.0001和>=而不是直接shadowSpace.z和==简单处理了下计算精度问题。

点击查看代码
for(int i = 0; i < dirLightNum; ++i){
    float4 shadowSpace = mul(_DirVPMatrix[i], input.w_pos);
    shadowSpace = shadowSpace / shadowSpace.w; //齐次除法
    float2 shadowTex_uv = shadowSpace.xy;
    if(shadowSpace.z + 0.0001 >= SAMPLE_TEXTURE2D(_DirShadowAtlas, sampler_DirShadowAtlas, shadowTex_uv).r){
       resColor += albedo;
    } 
}

使用SAMPLE_TEXTURE2D_SHADOW采样

SAMPLE_TEXTURE2D_SHADOW需要纹理,采样器状态和阴影空间的坐标作为参数。当坐标的z值小于阴影映射纹理中的深度值时,该宏返回1,这意味着该点距离光源更近,不在阴影中。否则,该宏返回值为0意味着该点在阴影中。

阴影痤疮(shadow acne)严重,阴影痤疮是由深度测试期间的舍入误差引起的。在阴影纹理中查找深度信息时计算的深度值通常与实际坐标的深度值不完全匹配。因此,从阴影纹理中查找到的深度值可能并非当前渲染中像素的深度。

级联阴影(Cascaded Shadow Maps,CSM)

暂时空缺先不做实现。用单张阴影纹理会导致阴影边缘的锯齿严重,为让使离镜头越近用更高分辨率的阴影纹理,会切分视锥体生成多张阴影纹理。

阴影痤疮(shadow acne)

针对这个问题,可以通过buffer.SetGlobalDepthBias来使得阴影效果更加自然。

能看到在阴影部分背光面采样依然有问题,这是由于深度偏移导致的,因为背光不需要计算是否光线被遮蔽所以这块无需关注。

不过,发现一个规律,就是深度变化率越大的地方,需要更大的bias才能去除阴影痤疮!

这块很好解释,阴影痤疮产生的原因就是深度值被量化后(在GetTemporaryRT设置为36位),深度数据是一个像素网格了,导致我们实际计算的值要么比采样的值大一点,要么会小一点,所以才需要一个偏差值(bias)。而和平行光夹角越大,说明这个像素网格覆盖的实际深度范围越大,那就需要更大的bias去处理这个深度范围。
所以理论上,如果平面完全和平行光垂直就不会有问题🤔,所以依次原理更进一步的有了斜率补偿偏移,其原理为光线和表面角度越大,偏移越大(即不用所有角度情况下都使用斜率最大时才需要的偏移)。当然所谓的Peter Panning依然存在,只是照射角度越垂直越轻微一些。

另一种方法是法线偏移(Normal Bias),既在生成阴影纹理时将顶点沿法线收缩一段距离,或采样前沿顶点偏移一段距离。但也都属于解决一个问题产生一个新问题的处理方法,看具体需求来选择。

又看了一些资料,对网格封闭物体,还可以正面剔除后在生成阴影纹理,效果比以上方法更好。
Shadow Map阴影贴图小讲
Shadow Map 原理和改进

软阴影

百分比渐近过滤(PCF)可以简单有效实现。

需要注意下的是,由于PCF的原理,相同的PCF块在不同的阴影纹理分辨率上效果会有区别。
同时,core RP的实现,例如5*5采样,并没有用到25给采样点,而是只使用了9个带权采样点,大概像下面这样,来提升渲染性能。

采样点分布
x - x - x
- x - x -
x - x - x

在部分情况下还需要考虑阴影“软”的程度与投射阴影的面产生阴影的面的距离的关系。


(图片来自RenderMan)

Shadow Map中涉及的主要部分都基本实现了(除了级联)。

posted @   溪溯P  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示