学习unity shader有一段时间了,打算开始陆续整理归纳一下技术要点,也方便日后的复习和参考。
第一个总结做水体效果。对于初学者来说,水体包含的知识点相对而言是比较多的,这里就以冯乐乐大佬的《Unity Shader入门精要》这本书中的内容为起点,逐步深入完善水面效果。
书中包含的内容有:
1.通过CubeMap生成环境纹理,计算水面反射
2.使用GrabPass获取屏幕的渲染纹理,模拟水面折射
3.用Frenel公式混合反射与折射颜色
后续个人完善内容:
1.基于Gerstner公式增加顶点动画实现波浪起伏
2.利用深度纹理实现浪花效果及调整水面到水底的可见度
3.利用噪声扰动实现伪焦散效果
4.使用ShaderGraph实现上述(Gerstner波以外)的效果
最终效果图如下(请无视场景里其它乱七八糟的东西x):
第一篇就简单介绍和回顾一下书里有的内容,有兴趣的朋友非常推荐买一本自己看看,写的非常详细。
首先为水面准备一张切线空间的法线贴图(随便百度一下就有,想自己做可以了解一下substance designer):
通常会使用一张平面网格作为水体网格,因此要达到真实世界水面的波澜起伏的效果,需要修改表面法线(即与模型上某点的切线垂直的向量)。在切线空间中,若不对法线加以扰动,法线方向就为(0,0,1),对x,y增加偏移量之后法线方向便会产生改变(这也是为什么切线空间法线贴图都是蓝色调的原因)。之后x,y的偏移量还可用于对渲染纹理采样的偏移模拟水下扭曲的效果。
利用对法线贴图采样得到的法线向量参与光照计算,可得如下效果:
表面起伏感就出来了,但是从侧面看其实还是个平面。追求真实感的话还需增加顶点动画。
之后就是水面的折射了,透过水面可以看到水下的物体。这部分效果通过对屏幕渲染纹理采样实现。
需要注意的是设置水面网格的渲染队列,需为Transparent,这样才能保证深度深于水面的物体正确的被渲染到纹理中。
利用屏幕坐标对渲染纹理采样,可看到水下物体(被水体平面遮挡的物体):
然后是水面的反射。
基本原理是放一个空物体在水面中心,之后通过脚本在空物体位置设置一个临时相机,并将这个临时相机拍摄到的环境图存到一张cubemap里。之后利用水面的反射方向对这张cubemap进行采样。
由于这个场景里只有个skybox,所以反射基本是天蓝色。
基本模块介绍完了,之后就是引入时间变量让法线贴图“动”起来了,计算公式如下:
此处采用两个相反方向的采样来达到水面波光粼粼的效果。
随后使用法线的xy偏移对渲染纹理的采样uv做扰动,实现水下扭曲效果。
最后将上述所有代码组合起来,并使用菲涅尔系数对反射和折射颜色进行插值,便能达到一个基本的水面效果:
到这部分源代码在作者的github都有上传:https://github.com/candycat1992/Unity_Shaders_Book/blob/master/Assets/Shaders/Chapter15/Chapter15-WaterWave.shader
下一篇介绍利用深度纹理实现浪花效果及调整水面到水底的可见度。