Shader实战案例
注:
本系列为shader实战的精品案例,这些是必须要掌握的
Shader实战案例
一、UV扰动之水纹效果
思路:利用噪声图信息偏移主UV图坐标,形成扰动效果。利用_Time平移顶点实现动画效果_,使扭曲纹理看起来在 X 和 Y 方向上滚动。
- 准备一张水面图,一张噪声图,噪声图可以看老唐shader,会讲如何制作。
效果图:

下面将一些:前置的知识点。
1.什么是噪声
它的本质是一种由数学算法公式生成的有规则性或可控的随机数据。
- 特性:
- 随机性:噪声数据本质上是随机的,这意味着它具有不规则性,无法通过简单的模式预测
- 平滑性:数据不会突然跳跃或变化,而是呈现出逐渐过渡的效果,适合模拟自然现象
周期性
:随着坐标的变化,噪声值会重复出现,形成一个封闭的循环。也可以通过调整噪声的范围或修 改算法来避免- 可调性:大多数噪声算法的输出是可以调整的,这种可调性允许你根据需求灵活地控制噪声的表现,适 应不同的视觉效果或逻辑处理需求 5. 多维性:能够处理多维数据,这意味着它们可以生成二维、三维甚至更高维度的噪声数据
- 注:因为有周期性的存在,它更像是一种周期函数
,使每个顶点偏离的值不同,从而达到扰动效果
。- 注:
如果每个顶点偏移的都一样,就会出现整体移动的效果
2.主要实现
注:CG语言。该代码块内部不支持识别CG语言。用C#代的,凑合看
1 | Shader "Unlit/Water" { |
(1)基础纹理的UV坐标
- 每个像素都有一个对应的UV坐标,用于映射到主纹理(
_MainTex
)上。- UV坐标的默认范围是 [0, 1],它决定了如何从纹理中采样颜色。
(2)引入扭曲纹理
扭曲纹理(
_DistortTex
)是一个灰度图,用于存储扰动信息。每个像素的灰度值表示该点的扰动强度。
(3)灰度值范围:
- 值为
0.5
表示无扰动。- 值小于
0.5
表示负扰动(向反方向偏移)。- 值大于
0.5
表示正扰动(向正方向偏移)。
(4)滚动扭曲纹理
通过时间变量
_Time.x
和滚动速度(_DistortTexXSpeed
和_DistortTexYSpeed
)动态调整扭曲纹理的UV坐标:
1 | i.uvDistTex.x += ((_Time.x + randomSeed) * _DistortTexXSpeed) % 1; |
这一步产生一个动画效果,使扭曲纹理看起来在 X 和 Y 方向上滚动。
(5) 扰动强度计算
1 | half distortAmnt = (tex2D(_DistortTex, i.uvDistTex).r - 0.5) * 0.2 * _DistortAmount; |
这里的计算逻辑:
tex2D(_DistortTex, i.uvDistTex).r
:采样扭曲纹理的红色通道值。- 减去
0.5
:将灰度值映射到[-0.5, 0.5]
的范围。- 乘以
0.2
和_DistortAmount
:控制扰动强度。- 这里面其实不映射也可以,(tex2D(_DistortTex, i.uvDistTex).r* _DistortAmount;这么写效果也能出来,只是上面写的要规范一些,实际测试容易点。其实就是把灰度图里面的灰度信息取出来然后偏移uv坐标就行了,主要就是计算每个顶点不同的偏移值然后叠加实现扰动效果。
注:
每个rgba通道都是独立的一张灰度图,然后里面灰度值都相同,只是扰动的轴不一样(比如r是扰动y轴),所以用哪个通道都无所谓。然后其实还可以,在别的通道上再去叠加另一组噪声,实现更复杂一点的效果。
(6)扰动应用到主纹理UV
1 | i.uv.x += distortAmnt; |
- 结果是主纹理的采样点发生了偏移,产生扭曲效果。
(7)为什么可以复用主UV坐标来生成扭曲纹理的UV坐标?
在Shader中,可以使用
TRANSFORM_TEX
宏来对UV坐标进行变换,包括缩放(Tiling)和偏移(Offset)。这个宏会根据纹理的_ST
属性(缩放和偏移)来调整UV坐标。因此,即使两个纹理的原始UV坐标不
同,通过适当的变换,也可以使它们对齐或产生特定的视觉效果
。
这种写法常常可以少去定义顶点着色器中appdata结构体中的变量,进而减少内存的开销。
之后的例子也会这么写。
名称 | 类型 | 说明 |
---|---|---|
_Time | float4 | t 是自该场景加载开始所经过的时间,4个分量分别是 (t/20, t, t2, t3) |
_SinTime | float4 | t 是时间的正弦值,4个分量分别是 (t/8, t/4, t/2, t) |
_CosTime | float4 | t 是时间的余弦值,4个分量分别是 (t/8, t/4, t/2, t) |
unity_DeltaTime | float4 | dt 是时间增量,4个分量的值分别是(dt, 1/dt, smoothDt, 1/smoothDt) |
二、UV扰动之描边燃烧
思路:通过采样判断像素四周Alpha值进行描边检测,利用原始透明度和轮廓透明度计算出外轮廓透明度值,之后将轮廓失真纹理进行时间滚动处理和扰动处理,最后利用lerp函数实现主纹理和轮廓的叠加混合。
效果图:

1.边缘检测算法
思想:通过采样当前像素四周的Alpha值,当相邻像素透明而当前像素不透明时,判定为轮廓边缘
采样方向 | 坐标偏移 | 检测目标 |
---|---|---|
左方 | i.uv - half2(destUv.x, 0) |
检测右侧相邻像素是否透明 |
右方 | i.uv + half2(destUv.x, 0) |
检测左侧相邻像素是否透明 |
上方 | i.uv + half2(0, destUv.y) |
检测下方相邻像素是否透明 |
下方 | i.uv - half2(0, destUv.y) |
检测上方相邻像素是否透明 |
- 当当前像素不透明(Alpha≈1)而相邻像素透明(Alpha≈0)时,对应方向的采样值≈0
- 四个方向的采样值相加后,有效轮廓区域的总值≈1(仅一个方向检测到边缘)
- 非轮廓区域的总值≈4(所有方向都检测到不透明像素)或≈0(所有方向都检测到透明像素)
二、主要实现
1 | Shader "Chapter1/chapter1_4" // 定义着色器名称为“Chapter1/chapter1_4” |