在上一篇文章中(https://blog.csdn.net/zhetianyun/article/details/103054640),我们实现了体素的环境光剔除功能,但是忽略了一个视觉上的小瑕疵:在体素的某一个平面上,由于顶点连接方向(顺时针和逆时针)的不同,导致各向异性问题的产生,这里用实际效果展示更形象些:
这是各向异性的效果。
这是没有各项异性的效果。
这两张图中,我们可以发现箭头所指的地方的区别:前者是直角,后者是45度斜角。明显后者的阴影效果更符合视觉效果。
各项异性问题的描述:
立方体是由六个面组成,每个面有两个三角形(计算机中最小的渲染单元)。想象如下的情形:
这是一个小方块的其中某一个面以及顶点id。按照我们上一篇文章的技术方案,假设现在只有顶点1正好是阴影顶点,2,3,4三个点都是正常的顶点。在第一个面上,因为顶点1被两个三角形所公用(△123和△134),所以这两个三角形都会被渲染成阴影,也就是上面第一张效果图:整个面都带有阴影。在第二个面上,因为顶点1只被第一个/或第二个三角形使用(△124和△234),所以只有△124被渲染成阴影,而△234的渲染中没有阴影,这就是上面第二张效果图所看到的。关于△124的阴影颜色,怎样从顶点1变化到顶点2 和顶点4,我们先暂时留作下次的作业。
综上所述,我们知道了体素的阴影出现各项异性的原因是,处于阴影位置处的顶点正好被该平面上的另外一个三角形所公用导致的。
解决方案
我们需要把这四个顶点移动一下就可以解决这个问题,解决的思路草图如下:
这样△123 和 △134 变成了△124 和△234。
代码实现过程
private void calculateAnisotropy()
{
List<Color> meshColor = new List<Color>();
cubeMesh.mesh.GetColors(meshColor);
int[] tri = cubeMesh.mesh.triangles;
List<int> quard = new List<int>();
for (int i = 0; i < tri.Length; i += 6) // 这里设置步长为6,是因为我们要拿到一个拿到一个完整的面,一个面有两个三角形,所以以6个顶点分割这个cube的所有顶点,最后我们构造了六个平面
{
quard.Clear();
if (!quard.Contains(tri[i]))
{
quard.Add(tri[i]);
}
if (!quard.Contains(tri[i + 1]))
{
quard.Add(tri[i + 1]);
}
if (!quard.Contains(tri[i + 2]))
{
quard.Add(tri[i + 2]);
}
if (!quard.Contains(tri[i + 3]))
{
quard.Add(tri[i + 3]);
}
if (!quard.Contains(tri[i + 4]))
{
quard.Add(tri[i + 4]);
}
if (!quard.Contains(tri[i + 5]))
{
quard.Add(tri[i + 5]);
}
// 获取了平面上的四个点 顺序是原始顺序,因为阴影保存在顶点color的alpha值中,所以根据对角两个顶点的 alpha 值加和判断平面是否需要 flip
if (meshColor[quard[0]].a + meshColor[quard[2]].a < meshColor[quard[1]].a + meshColor[quard[3]].a)
{
tri[i] = quard[3];
tri[i + 1] = quard[0];
tri[i + 2] = quard[1];
tri[i + 3] = quard[3];
tri[i + 4] = quard[1];
tri[i + 5] = quard[2];
}
}
cubeMesh.mesh.triangles = tri;
cubeMesh.mesh.RecalculateTangents();
}