You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Three.js中为3D模型特定表面精准投影纹理?

精准纹理投影到Three.js模型指定区域的解决方案

直接上最可靠的方案:自定义ShaderMaterial实现投影,比Raycasting手动改UV更高效精准,完全在GPU层面处理投影逻辑,适合固定区域的纹理投影需求。

分步实现说明

1. 定位目标表面

加载GLTF模型后,先找到要投影的子网格(比如模型里的某个Mesh):

const loader = new GLTFLoader();
loader.load('your-model.glb', (gltf) => {
  scene.add(gltf.scene);
  // 可通过name筛选目标网格,或遍历场景查找
  const targetMesh = gltf.scene.getObjectByName('target-face');
  if (!targetMesh) throw new Error('未找到目标网格');
  // 后续操作基于这个targetMesh
});

如果需要更细粒度的面级控制,可以给目标面添加自定义顶点属性,但通常子网格级别的定位已经满足需求。

2. 定义投影源(投影相机)

模拟一个投影相机,相当于纹理的"投影仪",通过位置、朝向控制投影角度:

// 正交相机模拟平行投影,也可用PerspectiveCamera实现透视投影
const projectorCamera = new THREE.OrthographicCamera(-5, 5, 5, -5, 0.1, 100);
projectorCamera.position.set(10, 10, 10);
projectorCamera.lookAt(targetMesh.position); // 对准目标表面
projectorCamera.updateProjectionMatrix();

3. 自定义着色器实现投影逻辑

替换目标网格的材质为ShaderMaterial,在着色器中计算投影UV并采样纹理:

顶点着色器

varying vec4 vWorldPosition;

void main() {
  vWorldPosition = modelMatrix * vec4(position, 1.0);
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

片元着色器

uniform sampler2D projectionTexture;
uniform mat4 projectorMatrix; // 投影相机的MVP矩阵
varying vec4 vWorldPosition;

void main() {
  // 将世界坐标转换到投影相机的裁剪空间
  vec4 projectedPosition = projectorMatrix * vWorldPosition;
  // 转换为纹理UV(NDC坐标转[0,1]范围)
  vec2 uv = projectedPosition.xy / projectedPosition.w;
  uv = uv * 0.5 + 0.5;

  // 超出投影范围的区域可返回原模型颜色或透明
  if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
    gl_FragColor = vec4(0.8, 0.8, 0.8, 1.0); // 示例用灰色代替
    return;
  }

  // 采样投影纹理
  vec4 texColor = texture2D(projectionTexture, uv);
  gl_FragColor = texColor;
}

材质初始化

// 加载投影纹理
const textureLoader = new THREE.TextureLoader();
const projectionTexture = textureLoader.load('your-projection-texture.png');

// 创建ShaderMaterial
const projectionMaterial = new THREE.ShaderMaterial({
  uniforms: {
    projectionTexture: { value: projectionTexture },
    projectorMatrix: { value: projectorCamera.projectionMatrixInverse.clone().multiply(projectorCamera.matrixWorld) }
  },
  vertexShader: // 上述顶点着色器代码,
  fragmentShader: // 上述片元着色器代码,
  transparent: true, // 纹理带透明通道时开启
});

// 替换目标网格的材质
targetMesh.material = projectionMaterial;

4. 实时更新投影矩阵(若投影源移动)

如果需要移动投影仪,在动画循环里更新uniform:

function animate() {
  requestAnimationFrame(animate);
  
  // 若投影相机位置/朝向变化,更新矩阵
  projectorCamera.updateMatrixWorld();
  projectionMaterial.uniforms.projectorMatrix.value = projectorCamera.projectionMatrixInverse.clone().multiply(projectorCamera.matrixWorld);
  
  renderer.render(scene, camera);
}
animate();

调试与优化建议

  • 目标网格定位困难:用scene.traverse遍历所有Mesh,打印mesh.name和顶点数量来识别;或在Blender里给目标面单独分组后导出。
  • 纹理拉伸/偏移:调整投影相机参数(正交相机的left/right/top/bottom,透视相机的fov),确保投影范围覆盖目标表面;也可在片元着色器中对UV做缩放偏移调整。
  • 遮挡处理:若需处理模型自身遮挡投影,可开启深度测试,或给投影相机添加depthTexture,在着色器中做深度对比。
  • 混合原材质:如需保留原模型基础颜色,可在片元着色器中混合纹理与原材质颜色:gl_FragColor = mix(originalColor, texColor, 0.8);

内容的提问来源于stack exchange,提问作者Abdellatif Mokafih

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 07:08:23