11 纹理采样与坐标变形
前置知识:第 7 章的图片比例与
texture2D、第 9 章的反向坐标查询、第 10 章的波浪相位。
本章示例:纹理坐标变形,完整源码。
学完本章能做什么
你将能区分网格 UV 与采样 UV,借助波浪和旋转改变图片外观,并预测坐标越界时的裁切、平铺及边界颜色。
1. 纹理采样究竟在做什么
顶点属性 uv 经光栅化插值,传到片元阶段成为 vUv。texture2D(uImage, vUv) 根据坐标读取纹理。三角形的位置没有改变,变化的只是从图片的哪个位置取颜色。示例图片从第 7 章复用,作为颜色纹理设置了 texture.colorSpace = THREE.SRGBColorSpace;采样后在 shader 中得到可用于线性 RGB 计算的颜色,输出前执行 #include <colorspace_fragment>。
为了看清变形,先把两种坐标分开命名:
// 片元阶段;vUv 是网格插值得到的 UV,sampleUv 是要查询的图片坐标。
vec2 sampleUv = vUv;
vec3 color = texture2D( uImage, sampleUv ).rgb;
把 sampleUv 改为 vec2(1.0 - vUv.x, vUv.y),图片会左右镜像,网格仍停在原处。
2. 用波浪扭曲图片
在每个 x 上改变纵向采样位置:
// 片元阶段;uAmplitude 的单位是图片 UV 高度。
const float TAU = 6.28318530718;
vec2 sampleUv = vUv;
sampleUv.y += uAmplitude * sin( TAU * uCycles * vUv.x - uSpeed * uTime );
vec3 color = texture2D( uImage, sampleUv ).rgb;
这里同一个屏幕 x 的所有像素,都从图片中相同幅度偏上或偏下的位置取色,因此图片的横线会变成波浪。采样坐标往上移,看到的图片内容通常往下移:这是第 9 章反向查询的同一原理。网格轮廓仍是矩形,也不需要增加分段;变形细节取决于像素数和纹理采样。
需要让图片内容视觉上向 +y 移动时,应把采样偏移写成负号。不要把“波浪线的高度”与“纹理采样偏移”的符号直觉混为一谈。
3. 用半径控制局部旋转
把图片中心附近旋转、外侧保持不变,可做一个旋涡:
// 片元阶段;将目标平面 UV 转为等比例的二维坐标。
vec2 point = ( vUv - 0.5 ) * vec2( uAspect, 1.0 );
float radius = length( point );
float influence = 1.0 - smoothstep( 0.0, uRadius, radius );
float angle = uTwist * influence;
vec2 sourcePoint = rotate2D( -angle ) * point;
vec2 sampleUv = sourcePoint / vec2( uAspect, 1.0 ) + 0.5;
中心的 influence 接近 1,半径外接近 0,中间平滑变化。反向旋转 sourcePoint 是为了让内容视觉上按 uTwist 的方向转。除以比例并加回 0.5,才能重新得到可用于图片采样的 UV。这里的 uAspect 是目标平面宽高比;图片本身若与目标比例不同,还要按第 7 章先决定拉伸、cover 或 contain。本示例故意以直接拉伸作为起点,聚焦采样坐标的变形。
4. 坐标越过 0~1 怎么办
波浪可能让 sampleUv.y 大于 1 或小于 0。示例提供三种在 shader 内显式实现的边界策略,便于观察:
// 片元阶段;示意代码。
vec2 clampedUv = clamp( sampleUv, 0.0, 1.0 ); // 边缘像素延伸
vec2 repeatedUv = fract( sampleUv ); // 每个整数区间重复一次
bool outside = any( lessThan( sampleUv, vec2( 0.0 ) ) ) ||
any( greaterThan( sampleUv, vec2( 1.0 ) ) );
第三种策略可让 outside 处显示提示色,而不读取图片。fract 对负数也取小数部分,如 fract(-0.2) == 0.8,因此能在负方向平铺。纹理对象本身也能用 texture.wrapS / wrapT 设置 ClampToEdgeWrapping 或 RepeatWrapping;示例选择在 shader 内处理,确保三种模式的差异集中在一处。fract 的跳变处可能出现明显接缝,线性过滤还可能混入边缘颜色;需要无缝平铺时,图片本身也应在相对的边界连续。
5. 纹理变形的三个层次
| 做法 | 改变什么 | 需要更多网格分段吗 |
|---|---|---|
修改 sampleUv |
从图片取色的位置 | 通常不需要 |
修改顶点 uv |
顶点间插值的采样坐标 | 复杂形变需要 |
修改顶点 position |
实际几何位置、轮廓 | 需要足够顶点 |
第 10 章的顶点波浪属于第三种;本章属于第一种。若图片只是“看起来扭曲”,不代表表面位置或几何法线已经改变。用同一波浪公式分别改变 position 与 sampleUv 可以组合效果,但必须明确各自的坐标单位和方向。
常见问题
- **波纹方向和预期相反:**纹理变形是反向查询;检查采样偏移的符号。
- **边缘拖出长条:**越界坐标被夹到边界,边缘像素不断重复;改用提示色或平铺来诊断。
- **平铺出现明显接缝:**图片两侧内容不连续,或
fract在整数边界发生跳变。 - **旋涡在宽平面上变形:**旋转前没有做目标比例修正,或回到 UV 时忘记除以比例。
- **纹理颜色比预期暗或亮:**确认颜色图片的
colorSpace,并在直接输出颜色的 shader 中执行颜色空间转换。
本章小结
纹理变形的核心是先为屏幕上的片元计算 sampleUv,再查询图片。波浪通过正弦偏移采样坐标;旋涡通过半径控制局部反向旋转。坐标越界策略、比例修正和纹理色彩空间都会影响最终画面。