今天查看Unity APV 源码时看其是如何对场景实现体素化的,网络上关于U6的APV系统讲解也非常少,写文章分享下。
APV系统在准备场景烘焙的数据之前,需要体素化场景用来加速场景SDF计算:因为计算每个三角面的距离非常复杂。APV使用了一种保守光栅化的方法实现了场景的体素化,大致步骤如下:
1.遍历场景中所有需要体素化的renderer的submesh,对于每个submesh:
2.在vertex shader内,将vertex Position (object space) => 根据x/y/z不同的投影轴投影到体素坐标系(world space)下;
3.geo shader内对原始的primitive边缘进行扩展,进行保守化光栅;
4.frag shader内进行AABB检测,将符合条件的体素化数据写入对应的 UAV (unordered access view)缓冲区的3D Tex内

为什么需要保守光栅化?
普通光栅化情况下,体素化一个三角面,我们只需要找到投影面积最大的投影面如下图所示:

但是,这样做会带来一个问题,部分primitive可能因为没有覆盖像素的中心点而出现漏洞:看一个2维的例子:


由于红色的primitive沿z轴的投影未覆盖任何像素(中心点),所以红色部分没有任何投影,这显然是不对的,我们需要扩展对应的像素。需要在shader中开启此功能。
Shader "Examples/CommandExample"
{
SubShader
{
// The rest of the code that defines the SubShader goes here.
Pass
{
// Enable conservative rasterization for this Pass.
Conservative True
// The rest of the code that defines the Pass goes here.
}
}
}
APV如何进行保守化光栅

VS中计算
在GS中计算扩展后primitive的AABB最大最小值:由于上面所说原因,除了找到最小的投影值外还需要扩展1像素大小。

rcp(_outputsize.x)为1体素大小.
FS中进行AABB测试:

测试通过则输出结果到对应的3D Tex中。
这里_Output[pos]是填充3DTex的alpha值,目的是为了给后续操作进行标记是否存在体素。
参考&引用
2.The Basics of GPU Voxelization
3.https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Graphics.SetRandomWriteTarget.html
4.https://docs.unity3d.com/6000.0/Documentation/Manual/writing-shader-conservative-rasterization.html
5.https://learn.microsoft.com/zh-cn/windows/win32/direct3d11/conservative-rasterization
6.https://learn.microsoft.com/zh-cn/windows/win32/direct3d12/conservative-rasterization