Games202课程的作业一,内容是基于ShadowMap实现硬阴影和PCF、PCSS的软阴影。
一、作业框架
作业框架与作业0基本相同,可以参考GAMES202 作业1框架代码结构分析 - 知乎 (zhihu.com)这篇文章,个人觉得非常详细,当然,框架其实并不复杂,花点时间就能搞懂。下面主要说一下需要注意的几点:
- 模型加载不出来,与作业0的问题相同,解决方法可参考我的上一篇博客Games202作业0及优化部分-优快云博客
- WebGL中的矩阵所采用的是列优先存储,这是其与大多数图形API不同的地方,在做矩阵操作需要注意,否则会得到意想不到的结果
- ShadowMap的存储采用的是RGBA四个通道,虽然每个通道都是一个float,但只有一个字节有效,也就是每个通道相当于只有一个字节,每个通道分别存储了深度值(float)的一个字节的精度,所以精度仍然不变,为32bit。将深度值拆解为颜色向量的pack函数位于shadowFragment.glsl中,pack函数采用了一系列位运算操作提取深度值的第 1 2 3 4 个字节并以vec4的形式返回,实现如下:
// pack函数用于将深度值的浮点数按照字节分别存储在RGBA中 vec4 pack (float depth) { // 使用rgba 4字节共32位来存储z值,1个字节精度为1/256 const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0); const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0); // gl_FragCoord:片元的坐标,fract():返回数值的小数部分 vec4 rgbaDepth = fract(depth * bitShift); rgbaDepth -= rgbaDepth.gbaa * bitMask; // 分别提取第1234个字节的内容, 并存储在RGBA的第一个字节中 return rgbaDepth; }相对应的unpack函数位于phongFragment.glsl中,实现如下:
float unpack(vec4 rgbaDepth) { const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0*256.0), 1.0/(256.0*256.0*256.0)); return dot(rgbaDepth, bitShift); }其原理也就不难理解了。
二、ShadowMapping实现
ShadowMap采用经典的two-pass方法实现,即先从光源出发对场景做一次模拟渲染,将得到的深度缓冲保存在阴影贴图中,然后再从相机出发进行渲染,在渲染时比较片元在light space下的深度与阴影贴图中的深度,从而判断该片元是否应该渲染为阴影。对于shadow acne现象,通过添加一个偏置进行缓解。
1.实现光源的MVP矩阵
在DirectionalLight.js中完善CalcLightMVP函数:

本文详细介绍了在GAMES202课程中如何基于ShadowMap实现硬阴影和PCF、PCSS软阴影,包括模型加载、光源MVP矩阵计算、ShadowMap的使用与优化,以及PCSS的原理和性能挑战。
最低0.47元/天 解锁文章
1612

被折叠的 条评论
为什么被折叠?



