
Roystan的卡通水着色器
只要跟着做就行,中间没有遇到bug,简直是享受。
基于深度的颜色
深度纹理
代码块中有一行声明了一个名为 _CameraDepthTexture 的 sampler2D 。这个声明使我们的着色器能够访问一个_未在属性中声明的_变量:摄像机的深度纹理 。_CameraDepthTexture 变量对所有着色器全局可用,但默认情况下不可用 ;如果在场景中选择 Camera 对象,你会注意到它附加了脚本 CameraDepthTextureMode ,并且其检查器字段设置为 Depth 。此脚本指示相机将当前场景的深度纹理渲染到上述着色器变量中。
1 | // CameraDepthTextureMode.cs |
从深度纹理获取线性深度值
1 | float existingDepth01 = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition)).r; |
tex2Dproj 会对纹理坐标做透视除法(除以 w 分量),而 tex2D 直接使用提供的 UV 坐标。
tex2D 用于普通纹理映射(模型UV),坐标是模型顶点提供的 UV 坐标。
tex2Dproj 用于投影纹理映射(阴影贴图、贴花、聚光灯投影),坐标是从光源或摄像机空间计算出的投影坐标(如 float4(lightSpacePos, 1) 或 ComputeScreenPos(o.pos))
想象你拿一个投影仪,把一张幻灯片投射到墙上:
- 投影仪就是“投影摄像机”,它的镜头决定了光线投射的方向和范围。
- 墙上某一点 P,它在投影仪的“胶片平面”上有一个对应的 2D 位置(就是 UV)。这个位置正是通过把 P 点变换到投影仪的裁剪空间,再做透视除法得到的。
- 然后,投影仪把该 UV 处的颜色照到 P 点上。
波浪
- 应用一个阈值来获得更接近二元效果的外观。
- 希望波浪强度在近岸或物体与水面交汇处增强,从而产生泡沫效果 。我们将根据水深调节噪声截止阈值来实现这一效果。
动画
- 添加动感:通过偏移用于采样噪声纹理的 UV 坐标
Edit Mode 下 Time 的更新不是实时的,所以动画看起来是卡顿的。
- 滚动效果就像一张纸在表面上拖动一样。我们将使用扭曲纹理来增加动态感。这种扭曲纹理类似于法线贴图 ,但只有两个通道(红色和绿色),而不是三个。
渲染法线缓存
根据水面法线与两者之间物体法线之间的角度来调节泡沫深度值(着色器中的
_FoamDistance)。为此,我们需要访问法线缓冲区 。Unity 内置了使用 DepthNormals 深度纹理模式渲染法线缓冲区的功能。该模式会将深度缓冲区和法线缓冲区打包到一个纹理中(每个缓冲区两个通道)。然而,这样做会导致深度缓冲区的精度不足以满足我们的需求;因此,我们将手动将法线渲染到单独的纹理中。
此脚本创建了一个与主摄像机位置和旋转角度相同的摄像机,但它使用替换着色器渲染场景。此外,它不会将场景渲染到屏幕上,而是将输出存储到名为
_CameraNormalsTexture的全局着色器纹理中。与我们上面使用的_CameraDepthTexture一样,此纹理可供所有着色器使用。利用点积的结果来控制泡沫量。当点积较大(接近 1)时,我们将使用比点积较小(接近 0)时更低的泡沫阈值。
透明
1 | Blend SrcAlpha OneMinusSrcAlpha |
Blend线决定了混合的具体方式。我们使用的是通常被称为 “普通混合”的混合算法,它类似于 Photoshop 等软件混合两个图层的方式。
-ZWrite Off可以防止我们的对象被写入深度缓冲区;如果_被_写入深度缓冲区,它将_完全_遮挡其后面的对象,而不是仅仅部分遮挡它们。
改变泡沫的颜色会产生不同的结果,红色泡沫会变成粉色,黑色泡沫会变成浅蓝色。
顾名思义,加色混合是将两种颜色混合在一起,从而产生更明亮的效果。这种方法非常适合用于发光物体,例如火花、爆炸或闪电。但我们想要将泡沫与水面混合——泡沫和水面本身都不发光,混合后的效果也不应该更亮;因此,加色混合并不适用于此。
叠加透明薄层
标准 Over 混合(Alpha Blend)和简单加法混合的核心区别在于:Over 混合考虑了前景的透明度(alpha),能实现“半透明覆盖”效果,而简单加法只是颜色相加,没有透明度概念。
1. Over 混合公式(用这个)
final.rgb = top.rgb * top.a + bottom.rgb * (1 - top.a)
final.a = top.a + bottom.a * (1 - top.a)
- 当
top.a = 1(泡沫完全不透明):final = top.rgb,泡沫完全覆盖水。 - 当
top.a = 0.5(泡沫半透明):final = top.rgb * 0.5 + bottom.rgb * 0.5,水和泡沫各贡献一半。 - 当
top.a = 0:final = bottom.rgb,完全看不到泡沫。
这种混合符合“叠加透明薄层”的物理直觉:颜色按比例混合,同时保留水的原始颜色。
2. 简单加法混合
1 | final.rgb = waterColor.rgb + surfaceNoiseColor.rgb |
- 泡沫颜色直接加到水上,无论泡沫的 alpha 是多少,都会提升亮度。
- 即使你想让泡沫半透明,比如
surfaceNoiseColor.rgb = (1,1,1) * 0.5,加法后也会使水变亮 0.5,而不是混合。 - 结果通常会导致过曝、颜色失真,且无法实现“泡沫覆盖水”的视觉效果(水依然完全可见)。
抗锯齿
1 |
|
拓展(原文的 Conclusion)
深度缓冲区可用于实现任何基于距离的效果,例如雾效或扫描效果。法线缓冲区用于延迟着色 ,即在所有渲染完成_后_ ,作为后处理步骤对表面进行光照。扭曲和噪波的应用几乎是无限的,它们可以用来修改网格的几何形状,类似于高度图的工作原理。
- 标题: Roystan的卡通水着色器
- 作者: 铁名_IronName
- 创建于 : 2026-04-21 14:35:35
- 更新于 : 2026-04-21 21:09:56
- 链接: https://blog.ironname.top/2026/Roystan的卡通水着色器/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。