URP 渲染实战 / 第 4 章 Shader 里的数学工具箱与程序化图案 / 案例 4.2
案例 4.2棋盘格、条纹与波点入门
目标:用 4.3 节的“编号 + 局部坐标”结构做三种最常见的重复图案,并支持旋转和抗锯齿。
Assets/Cookbook/Ch04/C04_02_Patterns.shader(节选:checker)
float Checker(float2 q) { // floor 得到格子编号,横纵编号之和的奇偶决定颜色;abs 处理负数格子 return abs(fmod(floor(q.x) + floor(q.y), 2.0)); }
棋盘格:横纵格子编号之和为偶数取一种颜色,奇数取另一种。fmod 对负数会返回负值,所以外面套一层 abs——当 UV 被旋转或平移到负数区域时,这一点就很重要了。
Assets/Cookbook/Ch04/C04_02_Patterns.shader(节选:stripes)
float Stripes(float2 q, float duty) { // 三角波:在条纹中心为 0、两条纹交界处为 1,宽度用阈值 duty 控制 float tri = abs(frac(q.x) - 0.5) * 2.0; float aa = fwidth(tri); // 一个像素内 tri 变化了多少 return 1.0 - smoothstep(duty - aa, duty + aa, tri); }
条纹:只用 x 坐标。abs(frac(x) − 0.5) * 2 把锯齿波变成三角波:条纹中心为 0,两条纹的分界处为 1,用一个阈值 duty 切开,就得到占空比可调的条纹。这里没有直接 step(0.5, frac(x)),正是为了得到一个“到边缘的距离”,好用 fwidth 做抗锯齿。
Assets/Cookbook/Ch04/C04_02_Patterns.shader(节选:dots)
float Dots(float2 q, float radius) { // 奇数行错开半格,得到更紧凑的“蜂窝”排布 float row = floor(q.y); q.x += 0.5 * abs(fmod(row, 2.0)); float2 cell = frac(q) - 0.5; // 每格内以中心为原点的局部坐标 float d = SdCircle(cell, radius * 0.5); return FillAA(d); }
波点:先用行号把奇数行向右错开半格,得到更紧凑的交错排布,再在每格中心画一个圆。圆用的是 SDF(4.6 节):length(c) − r,配合 FillAA 抗锯齿。
三种图案共用一个入口:先把 UV 以中心为原点旋转,再放大 _Scale 倍。旋转用的是 4.4 节的 Rotate2D。要注意“旋转坐标”与“旋转图案”方向相反:把坐标逆时针转 30°,看到的图案是顺时针转了 30°。

贴到 3D 物体上这些图案只依赖 UV,可以直接用在任何有 UV 的网格上;也可以把
input.uv 换成世界坐标的某两个分量(第 3 章案例 3.8 的思路),让图案不随物体缩放而拉伸。远处的高频条纹即便有 fwidth 也会因为“一个像素内包含多条条纹”而闪烁,这时可以根据 fwidth 的大小把条纹逐渐淡出成平均色——这是程序化纹理版的 Mip。