我在 WebGL 中使用 cubeTextures 预烘焙 nVidia 的 PCSS 工作了 2 个月。我已经成功地实时实现了野兽。预烘焙它最终也可以工作,但有一些明显的伪影。
简而言之,在下面我突出显示我拥有的主要工件并解释为什么我得到它,在一般情况下,如果每个人都尝试预烘焙软阴影,应该会遇到这种情况。总结这个线程,围绕这个问题的每一个建议都可以帮助我:
我们如何处理预烘焙的软阴影伪影?
让我们通过讨论软阴影而不是专注于 PCSS 来简化情况。这意味着我们正在使用一个 shadowMap,其中每个 texel 的可见性值范围为 0 到 1(而硬阴影生成的 shadowMaps 只有 2 个可能的值:0 或 1)。
由于我们不是实时的,我们必须固定一个视角,而不是在每一帧使用相机视角。我的软阴影是从光的角度计算的。为了构建它们,我:
- 计算带有所有阻塞器(遮挡器)的基本 shadowMap
- 在仅包含接收器的第二遍中,我使用 shadowMap 计算软阴影并存储每个像素的可见性
这给了我一个预先计算好的 shadowMap,我可以实时采样。
预烘焙软阴影的第一个限制是阻挡器不能成为接收器的一部分。想象一个充满遮挡物的房间,那么唯一的接收器肯定是房间。否则,您将失去房间网格上的一些阴影。原因是我们被困在了光的角度,所以如果我们也把它当作接收器,我们就看不到阻挡器背后的东西。
这种限制导致得到一个对房间具有良好可见性值的 shadowMap,但对于阻挡物上的阴影显然不是很好。我不介意在障碍物上获得正确的可见性值,我希望能够使用房间的阴影作为障碍物上阴影的近似值。但在实践中,您会在遮挡物上获得硬阴影,因为在遮挡物后面,房间阴影的可见性值是全黑的。
在最上面的情况下,我只把房间当作接收器。在底部情况下,我也使用拦截器作为接收器。您可以很容易地看到在这两种情况下都出现了问题。因为对于 shadowMap 的相同纹素,我们需要两个不同的可见性值:房间上的点是全黑的,而遮挡物上的点是半影。
我有很多想法来处理这个工件:
- 向着色器发送每个网格的 meshId 并评估此 id 以了解我们是否正在遮蔽阻挡器。
- 分别为每个拦截器制作一个 PCSS 通道,并在最后混合所有 shadowMaps。
- 分别为每个接收器制作 PCSS 通行证(考虑到拦截器)。
- 预先计算一半的计算并实时进行另一半。
- 从多个角度预计算 shadowMap,而不是从光的角度。
我在 1 和 2 上失败了。想法 3 似乎与想法 2 相同。4 很愚蠢,它不再是预计算了。而且我担心我将无法使想法 5 通用。
关于这个主题的文档真的很少。而且我发现的大多数文档都适用于理想场景,没有阻挡器遮蔽另一个阻挡器,就好像这不是通常的情况一样。所以也许这里有人已经面临这个问题或者对这个主题感兴趣?希望它也能帮助我之后的其他人。
不过,感谢您考虑这个问题。