Filament Froxel解析
题图来自Doom Eternal,因为本文的cluster forward rendering算法参考自Doom - Graphics Study。以下放出原文链接:
clustered forward rendering,DOOM的做法会将视锥体从三个维度进行划分,划分后生成的小方块原文中称之为【Cluster】,而在Filament代码中称之为【Froxel】。
目录
- Froxel结构划分
- 光锥体Froxel匹配
- 更新Froxel缓存数据
Froxel结构划分
Doom针对三维进行Froxel划分,x、y维采用线性划分,z轴采用logarithmic进行切割,切割后的片段称之为【Slices】。
Filament通过Froxelizer::update进行Froxel划分,首先来看x、y纬度的froxel划分:
froxelCountX = (width + froxelDimension - 1) / froxelDimension;
froxelCountY = (height + froxelDimension - 1) / froxelDimension;
而filament的z轴划分采取跟Doom的logarithmic方式进行划分:
z_{i} = zLightFar * 2^{ (i - froxCountZ ) * log_{2}(zLightFar / zLightNear) / (froxCountZ - 1)}
光椎体froxel匹配
- 首先是逐光源froxel化。froxel化的结果会存储在froxelThreadData中(filament默认开启clustered forward rendering的条件是光源数 > 2)
- 针对光源生成aabb包围盒(halfExtent为光源的影响范围)。针对aabb包围盒采用投影矩阵进行投影,zNear采用近平面和aabb.center.z + aabb.halfExtent.z较小的一个,zFar则采用aabb.center.z - aabb.halfExtent.z。计算zNear和zFar的一个原因是计算出光锥体的截屏面,因为这一步是针对光锥体进行froxel化。
- 确定光锥体的四个顶点的froxel范围。光锥体的四个顶点在Filament中分别为xyLeftNear、xyLeftFar、xyRightNear、xyRightFar,随后通过mClipToFroxelX 和mClipToFroxelY将投影坐标转换成xy两个维度的froxel下标。其中mClipToFroxelX起着将视口尺寸到froxel dimension尺寸转换的作用: mClipToFroxelX = (0.5f * viewport.width) / froxelDimension.x mClipToFroxelY = (0.5f * viewport.height) / froxelDimension.y
- 从光锥体的三个维度遍历froxel。单独从aabb包围盒来判断froxel是否包含光源太过粗糙,因为会存在一些froxel位于froxel空间点aabb包围盒内,但是并不与光源影响范围相交。因此filament通过三个维度去跟光源求交,如果相交,则对应的froxel会标记上对应的光源标记位。不同光源的标记位通过移位个数bit区别: froxelThread[fi++] |= LightGroupType(1) << bit
更新Froxel缓存数据
在上一步在froxel中对光源进行求交后,会把是否会受光源影响的结果记录FroxelShardedData中,随后需要生成着色器能够应用的Froxel缓存数据。大致架构如下:
//
// Light UBO Froxel Record Buffer per-froxel light list texture
// {4 x float4} R_U8 {index into RG_U16 {offset, point-count,