5.2 UV 坐标操作实战:平铺、滚动、旋转与极坐标


5.2 UV 坐标操作实战:平铺、滚动、旋转与极坐标

本节摘要:UV 是片元着色器里最好玩的变量——采样前对它做一点二维数学,贴图就会平铺、滚动、旋转甚至卷成漩涡。本节给出四套即抄即用的变换代码,拆解它们的适用场景,并提示变换叠加时的顺序陷阱。

动手改造 UV:每个效果只动一处

采样的输入是 UV,所以改造贴图根本不必碰纹理数据,只需在采样前加工坐标。四套变换,每套都是一两行:

vec2 uv = vUv; // 平铺:乘 scale,图案缩小 scale 倍并重复(wrap 须为 REPEAT) uv = vUv * 4.0; // 滚动:随时间平移,配合 REPEAT 形成无限循环动画 uv = vUv + vec2(uTime * 0.3, 0.0); // 旋转:先把中心挪到原点,转完再挪回去 vec2 p = vUv - 0.5; float c = cos(uAngle), s = sin(uAngle); uv = mat2(c, -s, s, c) * p + 0.5; // 极坐标:直角转极径与极角,圆形排布变条纹 p = vUv - 0.5; float r = length(p) * 2.0; float ang = atan(p.y, p.x) / 6.28318530 + 0.5; // 极角归一到 0 到 1 uv = vec2(r * 2.0, ang * 8.0);

每套变换单独吃下都有明确的视觉语义:平铺决定"图案密度",滚动决定"流向与速度",旋转制造"错位与动感",极坐标把直线的韵律掰成圆弧。水面流动、传送带、雷达扫描、装饰转盘——市面上一大批常见动效,全部是这四式的排列组合。

图:四类 UV 变换的视觉效果对照

图:四类 UV 变换的视觉效果对照

完整示例:流动的检修灯箱

把三套变换叠进一个完整的片元着色器,做一个"传送带灯箱":图案平铺、匀速滚动、边缘用权重压暗。

#version 330 core in vec2 vUv; out vec4 FragColor; uniform sampler2D uTex; uniform float uTime; void main() { // 第一步:平铺,图案密度乘四 vec2 uv = vUv * 4.0; // 第二步:横向滚动,速度每秒半个图块 uv.x += uTime * 0.5; // 第三步:采样(采样器 wrap 必须是 REPEAT) vec4 col = texture(uTex, uv); // 第四步:中心亮、边缘暗的权重,柔化整体观感 float vig = 1.0 - smoothstep(0.4, 0.75, length(vUv - 0.5)); FragColor = vec4(col.rgb * vig, col.a); }

注意权重的输入用的是原始 vUv 而不是变换后的 uv——暗角要贴着屏幕或模型固定住,跟着贴图一起滚就失去意义了。哪些量用原始 UV、哪些用加工后的 UV,是这类效果的隐性关键,写之前先想清楚每一层效果"锚定在哪"。

💡 关键直觉:UV 变换类效果的一切动画都来自"时间进 UV、UV 进采样"这一个入口。想反向播放就把速度取负,想变速就把常数换成曲线,想局部动就把速度场做成权重——入口不变,花样无穷。

顺序敏感与三类陷阱

二维变换与矩阵一样有次序问题:先平铺再滚动,滚动单位是"单个图块";先滚动再平铺,所有图块同步平移一整格。视觉差异微妙但真实存在,叠加两层以上时建议一层一个变量名,别挤在一行里链式运算。

三类高频陷阱记好。其一,旋转与极坐标必须先减中心再操作,否则图案绕屏幕角落转圈。其二,旋转后的 UV 会让 mip 层级选择失准——GPU 按屏幕邻域估算 UV 变化率,旋转打破了它的估算假设,远景可能出现偏糊或闪烁,必要时用渐变各向异性过滤兜底(下一节的主题)。其三,滚动动画的速度别写死在着色器里:uTime * 0.5 的 0.5 应当作为 uniform 传入,否则调速度要重新编译着色器,第 9 章讲调试时你会感谢这个习惯。

锚定:效果贴世界,还是贴屏幕

UV 变换写多了会撞上同一个问题:这个效果应该"粘"在物体上,还是"浮"在画面上?锚定对象选错,效果就会跟着不该动的东西一起动。三组对照把这个问题具象化。

贴世界的:水面波纹、传送带、砖墙污渍——效果属于模型表面,随物体一起移动旋转,用模型的 UV。物体转,波纹跟着转,物理直觉才对。

贴屏幕的:暗角、扫描线、镜头污垢、HUD——效果属于观察者,物体怎么动它不动,用 gl_FragCoord 或全屏 UV(后处理场景里就是 vUv 本身)。

需要混合的:雨滴沿挡风玻璃滑落——雨滴相对玻璃固定(贴世界),闪烁高光却随视角移动(贴屏幕)。实现上把两层效果分别算好再叠加,各用各的坐标源。

代码层面只差一个输入源的选择,但决定了效果的"物理身份":

// 同样的滚动条纹,两种锚定的代码只差输入源 vec2 uvWorld = vUv; // 跟着模型走 vec2 uvScreen = gl_FragCoord.xy / uResolution; // 钉在屏幕上 vec2 scroll = vec2(uTime * 0.3, 0.0); vec3 cWorld = texture(uTex, uvWorld * 2.0 + scroll).rgb; vec3 cScreen = texture(uTex, uvScreen * 2.0 + scroll).rgb;

动手实验建议照抄这段,把两个输出分别叠在旋转的物体上看差异——一个跟着转,一个纹丝不动,五秒钟建立终生的锚定直觉。

两个高频追问

滚动能做成镜像往复吗? 能,镜像滚动不依赖 REPEAT 环绕(图案始终在 0 到 1 内弹跳):uv.x = abs(fract(uTime * 0.25) * 2.0 - 1.0); 得到 0 到 1 到 0 的三角波——横幅广告、加载动效的常见选择。

中心缩放怎么做才不会连边缘一起缩? 直接乘系数会以原点为基准缩放,图案整体飘向角落。正确姿势是先减 0.5 挪中心、缩放、再加回 0.5——与旋转的套路同源。进阶玩法是让缩放系数随时间脉动:呼吸感图标、打击闪帧都用它。

本节要点回顾

  • 四式骨架:平铺乘法、滚动加时间、旋转减中心转矩阵、极坐标换算极径极角;
  • 锚定意识:暗角遮罩用原始 UV、图案动画用加工 UV,分层想清楚每层效果贴在哪;
  • 顺序即语义:平铺与滚动的先后决定滚动单位,多层变换分层写清;
  • 配置配套:REPEAT 环绕是平铺滚动的前提,速度参数走 uniform 别硬编码。

UV 玩转了,但放大缩小时的模糊与闪烁还没交代——下一节请出滤波与 mipmap 两位质检员。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U