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 可以组合效果,但必须明确各自的坐标单位和方向。

常见问题

本章小结

纹理变形的核心是先为屏幕上的片元计算 sampleUv,再查询图片。波浪通过正弦偏移采样坐标;旋涡通过半径控制局部反向旋转。坐标越界策略、比例修正和纹理色彩空间都会影响最终画面。

本章练习