这个水波效果并非真实的让顶点去运动而产生的的效果,而是通过法线扰动而产生的模拟效果,产生效果主要分为三个步骤,第一个步骤是计算折射,第二个步骤是计算反射,最后一个步骤是混合反射和折射的值。
首先是计算折射值,这个也是这三个中最复杂的一个部分,首先折射所采样的纹理是通过grabpass来实现的,grabpass可以在渲染完毕所有的不透明物体之后把渲染完毕的图形存储到一个纹理中,然后在下一个pass中就可以调用这个纹理了,而这个例子中的grabpass得到的纹理就是上图去掉水流的纹理。
然后我们需要获取屏幕坐标来对grabpass得到的_RefractionTex进行采样,想弄明白这个采样过程还是得回去好好看看渲染流水线。
首先使用o.scrPos = ComputeGrabScreenPos(o.pos);来获取屏幕坐标,不过得注意,这个坐标是齐次坐标,是没有除过w的,而且顶点着色器里面也不能去除w,因为顶点着色器得到的结果将会差值到片元着色器中,除以w之后会破坏它的线性,导致差值的结果变的不正确,因此,我们还需要在片元着色器中除以w来得到真正的屏幕坐标。
我们来观察一下ComputeGrabScreenPos的源码:
inline float4 ComputeGrabScreenPos (float4 pos) {
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
float4 o = pos * 0.5f;
o.xy = float2(o.x, o.y*scale) + o.w;
#ifdef UNITY_SINGLE_PASS_STEREO
o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
#endif
o.zw = pos.zw;
return o;
}
去掉那些对平台进行特殊处理的代码,这代码翻译过来的意思就是,
o.x = pos.x/2+pos.w/2,o.y = pos.y/2+pos.w/2,o.zw = pos.zw;
稍微变一下
o.x = (pos.x/pos.w+1)*pos.w/2(把(-1,1)范围的NDC映射到(0,1)的视口坐标)
把它最后再除一个pos.w就是最终的视口坐标了,也就是说把NDC转换为了除以w之前的视口坐标
接下来就是计算偏移的代码
float2 offset = bump.xy * _Distortion * _Refractio