【Unity3D Shader】学习笔记-水①
前言
水在各种游戏场景中还是比较常见的,图1展示的具有波光粼粼的水是不是有那么一回事呢?本篇就简单介绍这种动态水的制作步骤。
图1 波光粼粼的水面(未开启后期)
一、制作步骤
首先是自己弄了一个简单的地形场景,然后特地挖了这么一个坑,创建一个面片放在这个坑上面作为水面。需要用到的贴图有两张,一张水波动的法线贴图和一张实现水面波动的扰动图。具体面板上面的参数如下图所示:
图2 水体材质球面板参数
步骤一:获得切线空间的光照方向,视角方向
水面的波光粼粼的效果需要用到法线贴图,因此光照模型需要在切线空间中进行计算,先将灯光方向,摄像机方向统一转换到切线空间。
TANGENT_SPACE_ROTATION; // 模型空间的光照方向 - 切线 o.lightDir=mul(rotation,ObjSpaceLightDir(v.vertex)); // 模型空间的顶点到摄像机的观察方向 - 切线 o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
步骤二:扰动采样
波光粼粼的动画是用扰动贴图采样获得动态uv,然后用生成的uv进行法线贴图采样,从而得到动态的法线。其中i.uv是float4类型,包含了法线贴图和扰动贴图的uv数据,支持贴图缩放偏移操作。_WaveDirection也是float4类型,目前只用到xy来控制水流的方向和速度。
// 扰动采样,uv根据时间和朝向流速进行偏移 fixed4 noise = tex2D(_WaveTex, i.uv.zw + _WaveDirection.xy * _Time.y);
步骤三:融合法线
通过采样扰动贴图获得的动态uv采样两次法线,然后进行法线融合,从而产生用于光照计算的法线数据。关于法线融合这里就用了Unity自带的方法,其实还有好几种做法,可自行百度了解。
//根据扰动采样构造2个uv对法线进行采样,然后融合生成法线 fixed4 normal1 = tex2D(_WaterNormal, i.uv.xy + noise.xy); fixed4 normal2 = tex2D(_WaterNormal, i.uv.xy + noise.zw); fixed3 blend_normal = BlendNormals(normal1.rgb, normal2.rgb);
步骤四:光照模型
光照模型采样Phong光照模型,只计算了漫反射和镜面高光的贡献,可以加上环境光使整个水面再亮一点。
// 光照方向,视角方向归一化 fixed3 lightDir = normalize(i.lightDir); fixed3 viewDir = normalize(i.viewDir); fixed3 reflectDir = normalize(reflect(-lightDir, blend_normal)); // 漫反射 float NdotL = dot(blend_normal, lightDir); fixed3 diffuse = _WaterColor.rgb * NdotL * _LightColor0.rgb; // 镜面高光 float RDotV = dot(reflectDir, viewDir); fixed3 specular = _LightColor0.rgb * _WaterSpec.rgb * pow(saturate(RDotV), _WaterGloss); // 最终颜色 fixed3 col = diffuse + specular;
二、总结
Shader代码还比较简单,上面的效果还可以使用表面着色器来做,光照计算部分就可以省去,可以自行实现一下效果看看。