【Unity】Shader学习笔记
自己总结的小规律
数据类型
在Shader中,共有4种数据类型:
- 数学数据类型:
float
、int
、bool
;向量:Vector2
、Vector3
、Vector4
;矩阵:float2×2
、float3×3
、float4×4
- UV坐标
- Texture纹理:人物(物体)蒙皮、法线、Mask遮罩(黑白分明有规则的图案)、Noise噪声(黑白混乱的图案)
- 像素点的世界坐标
尤其需要注意UV坐标和纹理这两类:
数学数据类型、世界坐标自不用说,在学习的过程中经常容易把UV坐标、蒙皮、Mask遮罩搞混。
尤其是UV坐标,UV坐标在ShaderGraph
中是以图片展示其每个像素的数值,但其实UV坐标与Texture完全不一样。
- UV坐标是用于映射2D纹理到3D模型的坐标,举例来说:UV坐标是中介,类似地图,2D纹理需要根据UV坐标找到在3D模型上自己应该对号入座的位置
- 物体蒙皮:草地、金属棒球棒、墙面等纹理与形状关系不大的纹理可以随意复用;但是人体蒙皮的纹理与形状强关联,就不能随意复用
- 法线:一般也是人体这种与形状关联性强的纹理需要使用,用来计算光照
- Mask遮罩:黑白分明且有规则的图案,通常用来裁剪其他纹理,因为是黑白分明的图案,所以在计算的时候能发酵出很奇妙的结果
- Noise噪声:黑白混乱的图案,通常与其他纹理组合,无规则的黑白图案能实现随机的效果
每个作用不一样,在计算的时候要各司其职,不要混淆
总之
- UV坐标是一个正经的二维正方形,初始状态左下角为(0, 0),右上角为(1, 1)。其主要作用是告诉纹理该如何对号入座到3D模型上
- 纹理存储着纹理数据,每个像素点上存储着
RGBA
值用类来区分UV坐标和纹理:
1
2
3
4
5
6
7
8
9
10
11
12 class UV
{
int u;
int v;
}
class Texture
{
int u;
int v;
Vector4 color; // `RGBA`颜色数据
}
显隐/透明
显隐
该状态为默认状态。只有两个选择要么显示,要么隐藏。
- Alpha(float):与阈值比较,控制物体显隐
- Alpha Clip Threshold(float):显影阈值,Alpha大于这个数就显示,Alpha小于这个数就隐藏
除了需要设置Alpha外,还需要设置阈值。阈值没有默认值,如果不设置阈值,就无法控制显隐。
透明
需要将Surface
设置为Transparent
- Alpha(float):范围为[0, 1],从0完全透明,到1完全显示。如果设置了阈值需单独与阈值比较,比较方式与显隐一样
- Alpha Clip Threshold(float):显影阈值,Alpha大于这个数就显示,Alpha小于这个数就隐藏
只需要设置Alpha。如果设置了阈值,则还需与阈值相比较,控制显隐。
UV坐标
UV坐标是用于映射2D纹理到3D模型的坐标。在3D模型上的每个顶点都有一个对应的UV坐标,它告诉引擎在纹理上的哪个位置找到该顶点的颜色。
UV坐标的范围通常是从(0,0)到(1,1)。左下角是原点(0,0),右上角是(1,1)。
在三角形上,UV坐标会在三个顶点之间插值,以确保纹理正确地贴在整个三角形表面上。
节点
运算本身并不难,难的是需要将运算与图形相结合,并且不要把关键性的几个概念搞混
========数学运算符========
——加乘减除——
Add
(相加):叠加两个纹理
将两个Texture
相加,通常是将一个纹理与遮罩(Mask)相加:
白色
RGB
为1:同理,使颜色更亮黑色
RGB
为0:任何数加0等于本身,所以黑色部分显示内容不变
Multiply
(乘法)
- 数值乘法:如下图
- 纹理乘法:与遮罩相乘可以裁剪图片;与颜色相乘可以叠加颜色
NOTE:单数值乘法可交换位置,但是矩阵乘法不可交换位置
Subtract
(减法)
更改
Surface
为Transparent
可以设置透明度百分比,而不是单纯的显示/透明
这里只是一个扩展,继续我们的学习,将
Surface
改回Opaque
设置透明通道阈值Alpha Clip Threshold = 0
,意味着当Alpha
小于阈值使就会隐藏


如下图所示,物体的每个像素都会计算一遍这个减法,如果该像素的Y坐标减去1.75小于0,就会变透明
Divide
(除法):设置锐利度
红框①的内容:
- 原本U输出的内容是:从左到右逐渐远离0到达1
- 与
Float1 = 0.5f
相减:- 左边:
0 - 0.5 = 0.5
,依然是黑色 - 中间:
0.5 - 0.5 = 0
,变为黑色 - 右边:
1 - 0.5 = 0.5
,变为灰色
- 左边:
绿框②的内容:
- 与
Float2 = 0
相除:- 左边:
0 / 0 = 0
,依然是黑色 - 中间:
0 / 0 = 0
,依然是黑色 - 中间偏右:
0.00001 / 0 = 1
,变为白色 - 右边:
0.5 / 0 = 1
,变为白色
- 左边:
除以0时,若分子小于等于0 => 商为负无限大;若分子大于0 => 商为无限大。不建议除以0
从下图可以看出除法一般是用来设置锐利度的
从下图可看出除法在某些情况下还与光照有关
——比较——
Abs
(绝对值)
- input < 0 => output = -input
- input = 0 => output = 0
- input > 1 => output = input
Tips:
Relay
节点没有任何操作,只是为了Debug出图形
Clamp
(限制):控制显示的区域
- input < Min => output = Min
- Min < input < Max => output = input
- input > Max => output = Max
将输入的值,控制在范围内
如果与纹理坐标配合,就能控制显示的区域


Tips:
需要与Scale节点区分开
- Scale:裁剪掉其他区域
- Clamp:其他区域依然显示,只是显示的不是原来的纹理,而是单色
Saturate
(饱和):归一化
- input < 0 => output = 0
- 0 < input < 1 => output = input
- input > 1 => output = 1
常于Lerp
节点配合
从下图可以看出,在使用Saturate
节点之后,Y坐标大于1的值也是按1来计算的
Sign
(符号):硬化
- input < 0 => output = -1
- input = 0 => output = 0
- input > 0 => output = 1
Step
(比较):硬化
- A<B => output = 1
- A>B => output = 0
Floor
(去尾):向下取整
Fract
(取小数)
内部所作的事类似于
output = input - Floor(input)
——限制范围——
Remap
(映射):归一化
数据区间映射,将[Omin, Omax]上每个数映射到区间[Nmin, Nmax]上
$$
N_{xy} = \frac{N_{max} - N_{min}}{O_{max} - O_{min}} \times (O_{xy} - O_{min}) + N_{min}
$$
上述公式的Latex表达式:
1 | N_{xy} = \frac{N_{max} - N_{min}}{O_{max} - O_{min}} \times (O_{xy} - O_{min}) + N_{min} |
为方便理解,这里我们使用浮点数代替二维数,运作方式和结果是一样的
$$
\begin{split}
output &= \frac{1 - 0}{2 - 1} \times (1.5 - 1) + 0 \
&= 1 \times 0.5 \
&= 0.5
\end{split}
$$
最后输出的结果是0.5,与右边的颜色一样。
其实这里可以看出来,这里的数据很友好。Float1
的值正好将[1, 2]映射到[0, 1]上了
- 1 对应 0
- 1.5 对应 0.5
- 2 对应 1
小技巧:
在控制数值范围的时候,只用考虑
- 最小值如何才能达到目标最大值
- 最大值如何才能达到目标最小值
因为我们已经把最极端的两个例子给列出来了,其他的自然会满足需求
Lerp
(线性插值):融合两个纹理
融合两个纹理
—-反转—-
One Minus
(1-):1-input
out = 1 - input,对与UV坐标和遮罩很有用,可以将Texture纹理变为负片
- UV坐标:翻转两次,右上角翻转到左下角,左上角翻转到右下角


- 遮罩Mash:黑白颜色翻转
- 纹理:变为负片
Negate
(相反):相反数
将输入的值变为相反数
Tips:
世界坐标输出的Global Preview为球形
UV坐标和纹理是方形
——三角函数——
Tan
(正切)
ATan
(反正切)
——其他——
Scale
(缩放):裁剪区域
这个节点的Global Preview显示有问题,改变缩放数值后需要重新连接输入接口才能正常显示
- UV坐标:改变纹理缩放大小(与
Tilling
作用一样,再与offset
可以选择展示的位置)


- 纹理:改变颜色的亮度


========UV坐标========
Texture Coordinates
(UV坐标)
纹理坐标:定义纹理如何映射到3D资源上
Tiling
:纹理填充Offset
:偏移参数
从下图可看出,设置Tiling
为(2,2)之后,图片纹理在水平和垂直方向上各增加了一个纹理(前提是需要将Wrap Mode
设置为Repeat
)
输出完整的纹理坐标
- 左下角为(0, 0),为黑色
- 左上角为(0, 1),为绿色
- 右下角为(1, 0),为红色
- 右上角为(1, 1),为黄色
这里的颜色只是为了方便表述描绘二维数值的大小,没有什么特殊的含义,不要被颜色给迷惑了(顺带一提颜色正好对应着RGBA
中的(R, G)
)
输出纹理的横坐标
- 左边为0,右边为1
- 从左到右逐渐远离0,到达1
输出纹理的纵坐标
- 下边为0,上边为1
- 从下到上逐渐远离0,到达1
Panner
(平移):平移UV坐标
平移节点:根据Time
按指定Speed
移动UV坐标。若没指定速度,则使用默认速度
NOTE:使用的Texture必须将
Wrap Mode
设置为Repeat
========顶点========
Vertex Position
顶点位置坐标节点,输出每个顶点相对物体原点的坐标
- 对于基础形状,如球形的最上方为(0, 0.5, 0)
- 对于导入的物体,如人物,最上方为(0, 人物高度, 0)(并不是所有物体都是归一化的)
- 每个像素点的值不会随着物体的移动、旋转改变,但是缩放会改变
========Time========
Time Parameters
(时间)
时间参数节点:输出Unity内部经过的时间(以秒为单位)