导语
这篇教程来自 Unity 社区
@alexanderameye ,将一步步介绍怎样用 Unity 的 Shader Graph 创建一款漂亮的风格化水体着色器。我们的目标不是制作物理上精准的水,而是实现一种 实时、可控且好看的 风格化水体。
着色器文件可以在文末找到,一起来学习吧~
1.最初准备
⚠ 本教程使用 Unity 2022.2.6f1 和 Universal RP 14.0.6 制作而成。
在开始教程前,我们必须先准备好以下几件事。
渲染设定
在 URP 管线配置文件里启用 Depth(深度)和 Opaque(不透明)纹理 。
图注:URP 渲染设定
着色器与材质
新建一个 Unlit(不受光) 的 Shader Graph,将材质设为 Unlit ,表面类型设为 Opaque 。
图注:着色器设定
接着用这个着色器创建一份材质。
图注:风格化水材质
选中新材质,在 Advanced Options(高级选项)里把 Render Queue(渲染队列)改为 Transparent(透明) 。
图注:材质设定
场景
场景方面,我用了 Artkovski 的
The Illustrated Nature ,不过你可以用别的场景。
在场景里,我新建了一个平面,把材质添加了上去。
我们接下来就开始吧!
2.求出水深 (Water Depth)
制作水着色器最重要的一步是 确定水的深度 (Water Depth) 。我们会用深度值来驱动许多其他效果,比如变色、透明、和泡沫。
相对摄像机深度
大部分水着色器教程基本都用以下这套节点来计算由浅(1,白色)到深(0,黑色)的渐变深度。
图注:相对于摄像机的渐变深度
在这套节点里, Scene Depth (Eye) 节点会返回摄像机与水下某个物体的距离(按世界的距离单位米计算)。你可以把它想象成一条从摄像机射向水的射线,它在第一次击中水面下某个物体时便会停止。这条射线所达到的距离就是节点返回的距离。
图注:场景深度(Scene Depth)-屏幕位置(Screen Position)=水深(Water Depth)
我们真正在意的不是摄像机到水底物体的距离,而是 水面到水底物体的距离 。为此,我们能用 Screen Position (Raw) 节点的alpha通道来获取摄像机和水面间的距离,然后两段距离相减来取得代表 水深的距离 。正如上方图表所示。
图注:深度相减
最后我们 Divide (除以)一个深度范围/距离控制参数, Saturate (限值)输出(将范围限为0到1),然后用一个 One Minus (一减)运算在河岸求得白色值,在水深处得出黑色值。
图注:深度除法运算
需要注意的是,这个深度值 不是水的垂直深度 。如果你看向水面,射线会从眼睛向水的某一点发射,而这些节点所求出的距离是射线命中水面到命中水底所穿过的距离。意味着 水面上同一点所返回的深度值将由你看向水面的角度决定 。
这种效果/瑕疵可在下方摄像机晃动的动图中看到。这一点在这块石头上尤为明显,水面上同一点的颜色会根据观测角的大小而变黑或变白。
世界空间深度
个人来说,我不喜欢上边做的深度效果。接收到的深度值会随摄像机的移动而改变,而我更喜欢独立于摄像机位置的固定深度值。
💡这只是个人喜好,你完全可以继续用相对于摄像机的深度运算。
为了“修”这种相对于摄像机的深度,我另找了一种在游戏世界里计算深度的方法。
图注:世界空间深度衰减
这套节点的水深测量方式就如同用量尺垂直地伸入水体,量出的是水面到水底的距离。即使摄像机再怎么动,计算出的深度值也不会发生改变。
Depth Fade子图表
为了保持整张表干净整洁,把刚刚创建的所有节点划到一张 Depth Fade 子图表里不失为一个好办法。
图注:深度衰减的子图表
3.颜色与不透明度
在这一节我们将用刚刚求出的深度值为水面上色。
浅水与深水
有了水的深度后,我们能用它来控制水的颜色和透明度。直接把 Depth Fade 子图表的输出(一个0到1之间的值)连到 Lerp 节点,形成浅水色到深水色的渐变。
图注:浅水与深水色
深浅水颜色的逐渐变化能带来不错的效果。
拓展:HSV线性渐变
正如Alan Zucconi在他
讨论颜色插值的文章 里所解释的,在HSV空间而不是RGB空间计算渐变可以改善水的颜色。要在Shader Graph里做到这点,我们可以用一个自定义节点将RGB颜色转换成HSV,完成插值后再重新转换成RGB。
这个自定义节点可以像这样接入前边的图表里。
图注:HSV颜色线性渐变
这样对于水的颜色来说,深水和浅水中间地带的色彩会显得更加鲜艳。
拓展:海报化
海报化是一种很酷又好做的效果。我们只需要添加 Posterize 节点,用一个属性来控制步骤数即可。
海报化技术在游戏
A Short Hike 里有很好的使用例子。下图里的很多条色带就是海报化的效果。
额外提示:使用渐变色
如果你想要进一步控制水的颜色,可以使用 渐变纹理 来控制颜色。我将下例的渐变色转换成了一张256x1的纹理,再用[0, 1]以内的深度值进行采样。之所以要将渐变色转换成一张纹理,是因为Shader Graph并不支持渐变色属性。
这样一来,你就能做出颜色的硬转换或软转换了。
下方是把渐变色转换成纹理的代码:
using
UnityEngine
;
#
if
UNITY_EDITOR
using
UnityEditor
;
#
endif
using
System
.
IO
;
public
static
class
GradientTextureMaker
{
public
static
int
width
=
128
;
public
static
int
height
=
4
;
// needs to be multiple of 4 for DXT1 format compression
public
static
Texture2D
CreateGradientTexture
(
Material
targetMaterial
,
Gradient
gradient
)
{
Texture2D
gradientTexture
=
new
Texture2D
(
width
,
height
,
TextureFormat
.
ARGB32
,
false
,
false
)
{
name
=
"_gradient"
,
filterMode
=
FilterMode
.
Point
,
wrapMode
=
TextureWrapMode
.
Clamp
}
;
for
(
int
j
=
0
;
j
<
height
;
j
++
)
{
for
(
int
i
=
0
;
i
<
width
;
i
++
)
gradientTexture
.
SetPixel
(
i
,
j
,
gradient
.
Evaluate
(
(
float
)
i
/
(
float
)
width
)
)
;
}
gradientTexture
.
Apply
(
false
)
;
gradientTexture
=
SaveAndGetTexture
(
targetMaterial
,
gradientTexture
)
;
return
gradientTexture
;
}
private
static
Texture2D
SaveAndGetTexture
(
Material
targetMaterial
,
Texture2D
sourceTexture
)
{
string
targetFolder
=
AssetDatabase
.
GetAssetPath
(
targetMaterial
)
;
targetFolder
=
targetFolder
.
Replace
(
targetMaterial
.
name
+
".mat"
,
string
.
Empty
)
;
targetFolder
+=
"Gradient Textures/"
;
if
(
!
Directory
.
Exists
(
targetFolder
)
)
{
Directory
.
CreateDirectory
(
targetFolder
)
;

这篇教程详细介绍了如何使用Unity的ShaderGraph创建一个风格化的水体着色器,包括计算水深、颜色与不透明度、折射、泡沫效果、光照交互以及波浪和浮力的模拟。教程通过多个步骤和节点图示展示了具体实现方法,适合对ShaderGraph感兴趣的开发者学习。
最低0.47元/天 解锁文章
6884





