
本文详解如何利用 three.js 的 shadermaterial 与位移纹理(displacement map)驱动三张图像间的动态过渡,涵盖纹理采样、uv 插值、时间/交互控制及 fragment shader 中的混合逻辑,助你摆脱硬编码依赖,真正掌握 webgl 图像过渡原理。
本文详解如何利用 three.js 的 shadermaterial 与位移纹理(displacement map)驱动三张图像间的动态过渡,涵盖纹理采样、uv 插值、时间/交互控制及 fragment shader 中的混合逻辑,助你摆脱硬编码依赖,真正掌握 webgl 图像过渡原理。
在 Three.js 中实现多图之间的位移映射过渡(displacement map transition),核心不在于“读取像素数据到 CPU”,而在于在 GPU 着色器中,用一张位移纹理(displacement map)动态扭曲 UV 坐标,再以此采样三张源图像并按需混合。你当前代码中直接 texture2D(uTexture1, vUv) 的方式只是逐像素叠加(img1 * img2 * img3),并未引入位移逻辑或过渡控制,因此无法形成视觉上的“推拉”“撕裂”或“液化”式切换。
✅ 正确思路:位移 → UV 扭曲 → 分阶段混合
位移贴图本质是一张灰度图(如 PNG),其每个像素的亮度值(R/G/B 均可)被解释为 UV 偏移量。例如:
- 黑色(0.0)→ 不偏移;
- 白色(1.0)→ 最大偏移(通常需缩放,如
* 0.2); - 灰色(0.5)→ 居中过渡区。
你需要在 fragment shader 中:
- 从位移贴图采样当前 UV 对应的位移向量;
- 将该向量作用于原始 UV,生成扭曲后的
distortedUv; - 用
distortedUv分别采样三张图; - 根据统一插值参数(如
uProgress)分段混合:例如[0.0–0.33]显示图1主导,[0.33–0.66]图2过渡,[0.66–1.0]图3收尾。
? 修改建议:关键代码结构
首先,在 JS 中传入位移纹理和进度 uniform:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
// 加载位移贴图(建议使用单通道灰度图,或明确取.r)
const displacementTexture = new THREE.TextureLoader().load('/assets/displace.png');
displacementTexture.wrapS = displacementTexture.wrapT = THREE.ClampToEdgeWrapping;
planeMaterial.uniforms.uDisplacement = { value: displacementTexture };
planeMaterial.uniforms.uProgress = { value: 0.0 }; // 控制过渡进度 [0.0, 1.0]然后重写 fragment shader(精简清晰版):
varying vec2 vUv;
uniform sampler2D uTexture1;
uniform sampler2D uTexture2;
uniform sampler2D uTexture3;
uniform sampler2D uDisplacement;
uniform float uProgress;
void main() {
// 1. 从位移贴图采样偏移量(取.r 通道,归一化到 [-0.5, 0.5] 范围)
float disp = texture2D(uDisplacement, vUv).r;
vec2 offset = (disp - 0.5) * 0.4; // 0.4 是强度系数,可调
// 2. 计算三阶段扭曲 UV(模拟“展开→覆盖→收束”)
vec2 uv1 = vUv + offset * smoothstep(0.0, 0.33, uProgress);
vec2 uv2 = vUv + offset * smoothstep(0.33, 0.66, uProgress);
vec2 uv3 = vUv + offset * smoothstep(0.66, 1.0, uProgress);
// 3. 采样三张图
vec3 col1 = texture2D(uTexture1, uv1).rgb;
vec3 col2 = texture2D(uTexture2, uv2).rgb;
vec3 col3 = texture2D(uTexture3, uv3).rgb;
// 4. 分段混合(避免硬切,用 smoothstep 实现缓动)
vec3 finalColor = mix(
mix(col1, col2, smoothstep(0.33, 0.66, uProgress)),
col3,
smoothstep(0.66, 1.0, uProgress)
);
gl_FragColor = vec4(finalColor, 1.0);
}? 注意:
smoothstep(a,b,t)比mix()更适合过渡控制——它在区间[a,b]内提供平滑的 S 形插值,消除闪烁与跳变。
⚠️ 关键注意事项
-
纹理加载时机:确保
imgTexture1/2/3和displacementTexture均已needsUpdate = true(Three.js 会自动处理.load(),但若手动创建需显式设置)。 -
UV 范围安全:位移可能导致
uvX或uvY超出[0,1],建议在 shader 中加保护:vec2 safeUv(vec2 uv) { return clamp(uv, 0.0, 1.0); } vec3 col1 = texture2D(uTexture1, safeUv(uv1)).rgb; -
性能优化:避免在 fragment shader 中做过多分支;优先用数学函数(如
smoothstep,mix,clamp)替代if。 -
调试技巧:临时将
gl_FragColor设为vec4(disp)可直观查看位移贴图是否正确加载与映射。
✅ 总结
位移过渡的本质是 “用一张图控制另一张图的采样坐标”。你无需在 JavaScript 中提取像素数组——WebGL 的高效正来自让 GPU 在着色器中实时完成全部计算。掌握 texture2D + mix + smoothstep + uniform 四要素,即可自由构建任意数量图像间的可控、高性能、可交互的过渡效果。下一步可扩展:接入鼠标位置驱动 uProgress、添加时间动画、或用噪声函数生成程序化位移。
(完)

















