UnityShader——Wireframe

昨天无意中看到群里有个小伙伴发的一张截图中有个 VR/Wireframe 的 shader,晚上便抽空下载了5.6.3 的 bulit-in 看看源码,Unity 还是良心,直接把理论资料注释在了代码的开头,是NVIDIA的一篇文章,像DX11本身提供了FillMode = D3D11_FILL_WIREFRAME选项,可以直接渲染网格,但NVIDIA认为这样渲染有诸多问题,文章比较了该技术和以前的双 pass Wireframe 渲染在各方面的优劣。当然,文章里说自己的技术基本上各方面全面领先,我也没有再去找以前的双 pass 渲染相关资料了。

整体的思路很简单也很明确,只渲染离三角形边缘距离在一定范围内的像素,那么怎么知道像素离边缘的距离呢,NVIDIA 通过在Geometry Shader中计算三角形在裁减空间中每个顶点到对边的距离,即三角形三条边的高,高很容易得到,两条边叉乘得到两倍面积,两倍面积再除以边长即可得到高:


UnityShader——Wireframe_第1张图片

将三条边的高传入 fragment shader,则在光栅化插值后,我们便可得到任一像素离三条边的距离:


UnityShader——Wireframe_第2张图片

最后,在fragment shader中取最小距离与预先设置的阈值做比较即可得到下面的结果:


UnityShader——Wireframe_第3张图片

可以看到,锯齿感比较严重,因此 NVIDIA 又加了一个调和函数做插值:


UnityShader——Wireframe_第4张图片
UnityShader——Wireframe_第5张图片

NVIDIA 选择了 22x2 2 − 2 x 2 ,得到的效果如下:


UnityShader——Wireframe_第6张图片

最后,如果我们不取离三条边距离最小值,相对的,我们将代码改成
float minDistanceToEdge = min(i.dist[0], max(i.dist[1], i.dist[2])) * i.dist[3];
便可以得到没有对角线的网格:


UnityShader——Wireframe_第7张图片

更新一波
之前写的不画斜线的算法确实比较蛋疼,而且画出来效果也很挫,斜线会有一小段留在那,特别难看,前段时间突然想到这个东西,决定改一改,但智商捉急,思路依旧简单粗暴:
既然斜线通常都是三角形中边长最长的一条线, 那么干脆就在GS阶段把它的 index 标记出来然后传到 FS, 然后在 FS 算最小距离的时候,根据 GS 传过来的 Index 将斜边排除在外,这样,我们就得到了干净利落的正方形框线效果:


UnityShader——Wireframe_第8张图片

不过代码实在是有点挫的没法看了,GS 本身性能就不高,被我这么一整性能更低了, 暂时没想到特别优雅的实现方法, 继续to be continue…

你可能感兴趣的:(U3D)