【Unity3D Shader】学习笔记-水②
前言
具有深度的水,能够表现水面与岸边的过渡,对于清澈的小溪水面还可以看见水底的内容,本篇就介绍如何实现这种清澈的小溪水。
图1 清澈的水
一、制作过程
在顶点着色器中需要获得当前平面的屏幕坐标,使用内置方法进行获取,具体代码如下:
// 模型顶点在屏幕中的坐标 o.screenPos = ComputeScreenPos(o.vertex);
通过摄像机深度纹理获得当前水面以下的深度数据,具体的操作下面的代码已经给出。然后获得的效果应该跟图2类似,边上深度浅的地方应该呈现灰色,而深水区则是全白色。
// 提取深度值 - 非线性 float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)).r; // 获取视野线性深度值 depth = LinearEyeDepth(depth); // 减去当前面片屏幕深度值,获得水下深度 float depthInWater = saturate(depth - i.screenPos.w); // 用于查看结果 return fixed4(depthInWater, depthInWater, depthInWater, 1);
图2 水面深度
上面的边界区域太小,不是很明显。引入新的2个参数来控制水深的范围以及浅水区的强度。
// 除以参数z来控制中间白色部分的范围 float depthInWater = saturate((depth - i.screenPos.w) / _WaveDirection.z); // 对结果进行pow,使得小于0的部分更小,用于让浅水的地方更淡 depthInWater = pow(depthInWater , _WaveDirection.w);
图3 改变参数获得的新的深度
现在可以将这个深度数据作为最终水面颜色的Alpha值,这里需要注意Shader需要修改一下RenderType和Queue使用Transparent,并且设置Blend参数。现在的水面与地形交界处可以看见淡淡的地形了。
图4 将深度应用透明通道
引入一个新的参数用于控制整体的透明度,这样就可以得到一个简单的清澈水面效果。
// col 是上一篇水的最终颜色,额外加上了环境光。 return fixed4(col, depthInWater) * _WaterTransparent;
图5 整体透明
二、后记
虽然可以看到水底,但还不太真实。水面因为折射和运动应该会导致水底的东西看起来有一定扭曲,并且带有一定的焦散效果。本篇就先完成这样的效果吧,后面学习了再做更加真实的效果。