内置函数 — 着色器的数学工具箱

内置函数(Intrinsic Functions)是 CG/HLSL/GLSL 中最基础也最强大的组成部分。它们是 GPU 原生支持的数学运算,比你自己用循环实现的版本快几个数量级。

本课目标 逐个学习 Cg/HLSL 中最常用的内置函数,理解它们的数学含义、图形学用途,并用可视化的方式"看到"每个函数的效果曲线。

跨引擎通用性 本课讲解的所有函数在Cg、HLSL、GLSL 中都是通用的。无论是在 Unity、Unreal、Godot 还是自定义引擎中,abs() 永远是绝对值,lerp() 永远是线性插值。学会一次,终身受用。

1. Abs — 绝对值

float abs(float n);

float2 abs(float2 n);

float3 abs(float3 n);

float4 abs(float4 n);

**数学含义:**返回一个数的绝对值。任何负数都变成正数,正数不变。

| -3 | = 3

| -5 | = 5

| 6 | = 6

输入: -0.5 → 输出: 0.5

输入: 0.3 → 输出: 0.3

输入: -1.0 → 输出: 1.0

图形学用途:

  • 万花筒 / 镜像效果:对 UV 坐标减去 0.5 后取绝对值,产生对称效果
  • 三平面映射 (Triplanar Mapping):对法线取绝对值,判断投影轴向

2. Ceil / Floor — 向上/向下取整

Ceil(天花板)

float ceil(float n);

返回≥输入值的最小整数

ceil(0.1) = 1

ceil(1.7) = 2

总是”向上舍入”

Floor(地板)

float floor(float n);

返回≤输入值的最大整数

floor(1.56) = 1

floor(0.34) = 0

总是”向下舍入”

图形学用途:

  • Ceil — 缩放/放大镜效果:配合 Lerp 实现纹理局部放大
  • Floor — 卡通着色 (Toon Shading):将连续的颜色梯度分成离散色块

3. Clamp — 限定范围

float clamp(float a, float x, float b);

// a = 最小值, x = 要限制的值, b = 最大值

**数学含义:**将值 x 限制在 [a, b] 范围内。如果 x < a 返回 a,x > b 返回 b,否则返回 x。

输入 x = -0.5,范围 [0, 1] → 输出 0

输入 x = 0.5,范围 [0, 1] → 输出 0.5

输入 x = 1.5,范围 [0, 1] → 输出 1

图形学用途:

  • 防止颜色饱和:将计算结果限制在 [0, 1] 之间
  • 点积结果钳制:将 [-1, 1] 的法线·光照结果 clamp 到 [0, 1]

4. Step / Smoothstep — 阶跃与平滑过渡(重点)

4.1 Step

float step(float x, float edge);

// 当 edge ≥ x 时返回 1,否则返回 0

UV.y < 0.5 → 黑色 (0)

UV.y ≥ 0.5 → 白色 (1)

──────── 阶跃 ────────>

0.0 ─── edge=0.5 ─── 1.0

黑 | 白

4.2 Smoothstep

float smoothstep(float a, float b, float edge);

数学含义:与 step 类似,但在 a 和 b 之间产生平滑的 Hermite 插值,而不是硬边缘。

t = saturate((edge - a) / (b - a))

return t² × (3 - 2 × t)

0.0 ──── a ══ 平滑过渡 ══ b ──── 1.0

黑 渐变上升 白

对比StepSmoothstep
边缘硬边缘(二元值:0 或 1)平滑过渡(连续值 0→1)
参数数量2 个(x, edge)3 个(a, b, edge)
典型用途二元遮罩、裁剪边缘羽化、圆形柔化

5. Lerp — 线性插值(重点)

float lerp(float a, float b, float n);

// 返回 a + n × (b - a)

**数学含义:**在 a 和 b 之间按 n 比例插值。n=0 时返回 a,n=1 时返回 b。

t=0.0 → 纯黑

t=0.5 → 50% 灰

t=1.0 → 纯白

图形学用途:

  • 纹理混合 (Texture Blending):在两张贴图之间渐变
  • 淡入淡出 (Crossfade):在两种皮肤/颜色之间平滑过渡
  • 任何需要”在两个值之间渐变”的场景

Lerp 的三个参数 第三个参数 n 不限于 [0, 1]。如果 n < 0,lerp 会外推到 a 之外;如果 n > 1,会外推到 b 之外。这在某些特效中很有用。

6. Min / Max — 最小值与最大值

Min

float min(float a, float b);

返回 a 和 b 中较小的值

min(3, 7) = 3

Max

float max(float a, float b);

返回 a 和 b 中较大的值

max(3, 7) = 7

图形学用途:

  • Clamp 的基础clamp(a, x, b) = max(a, min(x, b))
  • 漫反射光照max(0, dot(normal, lightDir)) 防止背光面出现负值

7. Frac — 小数部分

float frac(float n);

// 返回 n - floor(n)

图形学用途:

  • 平铺 / 重复图案:对 UV 乘 N 后取 frac,产生网格状重复
  • 噪声 / 随机:在程序化生成中创建重复单元

8. Length — 向量长度

float length(float n);

float length(float2 n);

// 内部实现: sqrt(dot(n, n))

**数学含义:**返回向量的模(长度),即从原点到该点的距离。

图形学用途:

  • 绘制圆形length(UV - center) < radius
  • 距离场:计算片元到某点的距离

9. Exp / Exp2 / Pow — 指数与幂

Exp

float exp(float n);

eⁿ(e ≈ 2.71828)

exp(2) ≈ 7.389

Exp2

float exp2(float n);

2ⁿ

exp2(3) = 8

Pow

float pow(float x, float n);

x 的 n 次幂

pow(3, 2) = 9

图形学用途:

  • Gamma 校正pow(color, 2.2) 将颜色映射到 Gamma 空间
  • 光照衰减pow(max(0, dot(N, L)), _Gloss) 高光计算
  • 噪声和程序化纹理:指数函数用于创建平滑衰减

10. Sin / Cos / Tan — 三角函数(重点)

10.1 Sin / Cos

float sin(float n);

float cos(float n);

sin(0) = 0

sin(π/2) = 1

sin(π) = 0

sin(3π/2) = -1

cos(0) = 1

cos(π/2) = 0

cos(π) = -1

cos(3π/2) = 0

图形学用途:

  • 旋转矩阵:sin 和 cos 构成旋转矩阵的基础
  • 循环动画:sin(_Time.y) 产生 -1 到 1 的循环值
  • 波动/呼吸效果sin(UV.x * freq + _Time.y)

10.2 Tan

Tan(θ) = Sin(θ) / Cos(θ)。它的值域比 [-1, 1] 大得多,因此图像有尖锐的峰值。

图形学用途:

  • 全息投影线abs(tan(uv.y * _Sections)) 生成扫描线效果

11. 函数速查表

函数数学含义典型用途跨引擎
abs(n)绝对值镜像、万花筒Cg/HLSL/GLSL
ceil(n)向上取整缩放效果Cg/HLSL/GLSL
floor(n)向下取整卡通着色Cg/HLSL/GLSL
clamp(a,x,b)限制在[a,b]防止饱和Cg/HLSL/GLSL
step(x,edge)阶跃函数硬边缘遮罩Cg/HLSL/GLSL
smoothstep(a,b,x)平滑阶跃柔化边缘Cg/HLSL/GLSL
lerp(a,b,n)线性插值纹理混合Cg/HLSL/GLSL
min(a,b)/max(a,b)最小/最大值漫反射、求交Cg/HLSL/GLSL
frac(n)小数部分重复图案Cg/HLSL/GLSL
length(v)向量长度圆形、距离场Cg/HLSL/GLSL
exp(n)/exp2(n)指数Gamma、衰减Cg/HLSL/GLSL
pow(x,n)幂运算高光、GammaCg/HLSL/GLSL
sin(n)/cos(n)/tan(n)三角函数旋转、波动Cg/HLSL/GLSL

12. 练习与回顾

思考题

  1. Abs + UV:为什么 abs(i.uv.x - 0.5) 能产生镜像效果?如果把 UV.y 也加上呢?
  2. Step vs. Smoothstep:在什么场景下应该用 step,什么场景用 smoothstep?
  3. Lerp 外推:如果 lerp 的第三个参数是 -0.5,会发生什么?
  4. 三角函数:sin 和 cos 的核心区别是什么(相位差)?为什么旋转矩阵需要同时使用两者?
  5. Length 画圆length(p - center) - radius 返回负值时表示什么?
  6. 动手实验:在 Unity 中创建两个纹理材质,用 lerp + 时间变量实现自动交叉渐变。

13. 教材对照

教材对照

本课对应《The Unity Shaders Bible》第 I 章 (p.127-173):

  • 4.0.7 内置函数概述 (p.127)
  • 4.0.8 Abs 函数 (p.127-132)
  • 4.0.9 Ceil 函数 (p.132-137)
  • 4.1.0 Clamp 函数 (p.137-141)
  • 4.1.1 Sin / Cos 函数 (p.142-146)
  • 4.1.2 Tan 函数 (p.147-150)
  • 4.1.3 Exp / Exp2 / Pow 函数 (p.150-152)
  • 4.1.4 Floor 函数 (p.152-156)
  • 4.1.5 Step / Smoothstep 函数 (p.157-160)
  • 4.1.6 Length 函数 (p.160-164)
  • 4.1.7 Frac 函数 (p.164-168)
  • 4.1.8 Lerp 函数 (p.168-171)
  • 4.1.9 Min / Max 函数 (p.172-173)