读本书第一遍的时候,好多知识点一知半解就过去了,没有仔细思考。读本书第二遍的时候发现很多知识点都能够串联起来了,也因此解决了很多之前对本书困惑的地方。
1.reflect(i,n)函数在高光反射模型和在计算反射的时候的应用
计算高光反射模型的时候,我们使用reflect(i,n),i为入射光线,n为法线。但是由于函数定义入射光线是由光源发出到点的,所以通常我们会写
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
但在实现反射效果的时候,我们写
fixed3 reflectDir = reflect(-worldViewDir, worldNormal);
理解起来应该是说,在计算高光反射模型的时候,是计算哪些沿着完全镜面反射方向被反射的光线。由于光是从四面八方射到物体的,其中当然也包括反射到摄像机的光线了。所以我们会用普遍的lightDir入射光线,来计算反射光线。
当在实现反射效果的时候,我们需要得到的是,从摄像机视角看过去的反射效果是正确的效果。所以我们需要的是,物体反射到摄像机的那个“光”,那个是我们需要的反射光线,而那个就是ViewDir。因此由入射光线由光路可逆原则,就是 -worldViewDir。
2.UnityShader中的玻璃效果
本书中,使用GrabPass来实现玻璃等透明材质的模拟。首先,书里实现了玻璃的反射+折射两个效果。并且使用GrabPass来对物体后面的图像进行复杂的处理,比如法线的模拟折射效果,不再是简单的与屏幕颜色进行混合。(之前是使用lerp函数进行混合,lerp(diffuse, refractionColor, _refarctionAmount))
先看反射,反射的实现方式与之前相似的。首先,我们使用reflect函数计算出反射方向,然后使用这个三维的纹理坐标来对cubemap进行纹理采样。采样的结果与主纹理颜色相乘就是反射的最终结果了。这就是只计算反射的结果。
然后是折射。之前我们也是用refract函数来计算折射方向,不过注意的是refract传入的是三个参数,refract(i,n,eta),eta就是入射ior/折射ior,i和n都是经过归一化的噢。以前是模拟不透明物体产生折射的时候上面的效果。现在要模拟的玻璃效果,就是说透过玻璃,我们看到里面的物体是有经过“折射”的感觉的,这点我们要模拟出来,就不能再是简单的把折射结果与屏幕颜色进行混合了。
然后我们看GrabPass,这边要清楚的是GrabPass是抓取当前的屏幕图像并把它储存在一张你命名的RT中。GrabPass通常是用来渲染透明物体的,但是我们需要在subshader的标签中写明,“Queue” = “Transparent”,但是"RenderType" = “Opaque”,为什么呢,这是因为我们希望不透明物体都在这个物体渲染之前被先渲染完了,这样渲染这个物体的时候抓取的屏幕图像才是正确的屏幕图像。
GrabPass把抓取到的屏幕图像储存在一张与屏幕分辨率相同的RT中。那么,屏幕图像当然要用屏幕坐标来采样啦。屏幕坐标怎么得到呢,记得之前我们学过如何获得片元的屏幕坐标,其中有一种就是利用ComputeScreenPos函数,但是利用这个函数我们还需要在片元着色器中手动进行齐次除法,才能得到最终的屏幕坐标/视口坐标(即左下角是(0,0),右上角是(1,1))。 在这个例子中ComputeGrabScreenPos函数来得到抓取屏幕图像的采样坐标。那这两个函数有什么区别呢?
首先我们要说的是,渲染平台的差异性。对于屏幕坐标系,OpenGL的左下角为(0,0),而DirectX的左上角为(0,0)
书中的原话是:
大多数情况下,这样的差异并不会对我们造成任何影响。但当我们要使用渲染到纹理技术,把屏幕图像渲染到一张渲染纹理中时,如果不采取任何措施的话,就会出现纹理翻转的情况。幸运的是,Unity在背后为我们处理了这种翻转问题——当在DirectX平台上使用渲染到纹理技术时,Unity会为我们翻转屏幕图像纹理,以便在不同平台上达到一致性。
Unity遵循OpenGL的规定,例如当我们使用渲染到RT技术的时候,且我们需要渲染到DirectX平台的时候,Unity在背后都为我们处理了这个翻转问题。具体操作是,Unity会通过翻转投影矩阵Projection Matrix 从而翻转 Ren