- 阴影失真:阴影的不真实感
条纹样式:
- 首先理解采样原理:同光的视角下,渲染一张深度图,每个像素,存储同一射线下的深度值(不断更新深度缓冲的结果),即最近片段的深度。
- 接着,每个片段,T变换到光的视角下,从对应的深度贴图采样,比较自身的深度值,和深度贴图的深度值,、这便是阴影计算的过程
- 但是,深度贴图受限于分辨率,在距离光源比较远的情况下,多个片段可能从深度贴图的同一个值中去采样,
- 由于每个像素的深度值,为映射到同一像素范围内片段的中心点,那么映射到同一像素范围内的,某些Fragment.z > 中心点的z,就会被判断为阴影中,而某些Fragment.z < 中心点的z,判断为非阴影,所以,同一个映射范围的片段,有暗有亮,产生不合理的效果
- 解决:偏移:可以让 表面的深度 / 深度贴图 应用一个偏移量(0.005f),这样同一范围的z深度,计算比较后的结果都是一致的,
- 但是,这仅针对大部分情况,有些表面坡度很大,仍然会产生阴影失真
- 原因:当光线和片段的法线 角度比较大,Fragment.z的值也会比较大,我们将z值偏移0.005f,并不能使它的z>中心点的z,
- 解决:应用更大的偏移量:float bias = max(0.005 * (1.0 - dot(normal, lightDir)), 0.005);
- 这行代码根据光线和片段的法线 角度,决定具体的偏移值
???悬浮Peter panning:
- 当使用阴影偏移,我们会发现阴影处于不正确的位置显示(虽然没有了条纹),对于原本应处于阴影的区域,却被判定为非阴影,不再计算阴影,
- 原因:应用了偏移bias,虽然几乎可以保证同一映射范围的,片段判定一致,但是假如原本应处于阴影中,也就是深度贴图有更近的深度值,bias后片段的z深度,其实有一定可能比这个深度贴图的z深度还要小了,所以判定为非阴影
- 书中的解决方案:当渲染深度图时候使用正面剔除(front face culling):也就是对深度贴图的深度值做了偏移(更远,值更大)
- ???深度贴图深度值更大,不是越容易z - bias 小于<深度值(更高了)
- 我认为这不是正确的解决方案,在我应用了面剔除,真的几乎没有阴影渲染了,并且源码中也没有用glCullFace(GL_FRONT);
- 限制:只对内部不会对外开口 / 非单独平面 的实体物体有效,因为仅一个平面,剔除会完全移除它
采样过多
- 我们发现地面上有一大块的阴影,超出光的视锥的投影坐标比1.0大,这样采样的深度纹理就会超出他默认的0到1的范围
- 这是因为:超出光的视锥(和相机视锥不同)的片段一律被认为是处于阴影中,不管它真的处于阴影之中
- 解决:让所有超出深度贴图的坐标的深度值是1.0,储存一个边框颜色,纹理环绕方式设置GL_CLAMP_TO_BORDER(当纹理坐标的值超出[0, 1]范围时,OpenGL会使用边框颜色作为纹理的颜色。)
- 仍有一部分是黑暗区域,那里的坐标超出了光的正交视锥的远平面。
- 解决:强制将shadow的值强制设为0.0非阴影
PCF
- 放大看阴影,会看到明显的锯齿状
- 原因:深度贴图有一个固定的分辨率,多个片段对应于一个纹理像素,那么这几个片段便得到的是同一个阴影,而非各自的阴影,这就会产生锯齿边。
- 方案:增加深度贴图的分辨率 / 尝试尽可能的让光的视锥接近场景(像素从一定范围的片段映射,当越接近,可映射范围就小,所以原本的某部分片段,产生了更多的采样坐标) / PCF
- PCF(percentage-closer filtering):深度贴图中多次采样,所有的次生结果接着结合在一起,进行平均化,我们就得到了柔和阴影。
- 根据textureSize(纹理对象,mipmap级别)(0:具有最高的分辨率和最多的细节),返回给定采样器纹理的mipmap级别的宽和高,再用1除以它返回一个单独纹理像素的大小