要做到简单的非真实渲染,需要注意物体轮廓线,高光的渲染。
渲染轮廓线
渲染轮廓线在RTR中归纳出了5种方法,笔者简要理解了其中4种的原理。
4种方法:
- 渲染两次,第一次渲染物体背面,并用一些技术使它轮廓可见。
比如用轮廓线的颜色渲染整个背面,并把模型顶点沿着法线方向延伸出来一点点让我们从正面看得见。
第二次渲染正面,正常渲染就好。
* 基于图像处理的轮廓线渲染
得到当前视角的深度图和法向量图,找出突变位置判断边缘
基于轮廓边检测的轮廓线渲染。
检查相邻两个三角面片是否一个面向正面,一个面向背面。
方法为,另 v 为视线到该两个三角形重合边的任意点的方向,另n0 和 n1 为两个相邻三角形的法向量。则若 (n0⋅v>0)≠(n1⋅v>0) ,表示该边为轮廓线混合上述方法
对于第一种方法的实际操作
对于我们提到的第一种方法,对于背面
viewPos = viewPos + viewNormal * _Outline;
但是对于一些内凹的模型,可能发生背面面片遮挡住正面面片的情况
这里指的应该是从背面看是凹进去的
为了解决这个问题,我们对顶点法向量(决定深度的量)进行一定处理,让它等于定值,然后把法线归一化再对顶点进行扩张
viewNormal.z = -0.5;
viewNormal = normalize(viewNormal);
viewPos = viewPos + viewNormal * _Outline;
渲染高光
卡通上的高光分解会更明显,简单的想法是我们求出高光后,判断高光是否高于某个阈值,如果高于,则渲染为高光,否则高光分量为0。
但是像这样分界会造成高光边缘锯齿化,我们希望高光的分界尽可能平滑,所以我们在高光边界处定义一个小区间,在小区间进行插值来消除锯齿。
float spec = dot(worldNormal,worldHalfDir);
//小区间插值
spec = lerp(0, 1, smoothstep(-w, w, spec - threshold));