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

ThreeJS 如何区分相同纹理的不同模型网格边界

ThreeJS 同纹理多模型边界区分解决方案

你此前使用的常规轮廓后处理依赖模型本身的法线、边拓扑信息,Sketchup导出的不规整网格会导致描边异常,以下3种方案均避开了对网格拓扑的依赖,可以解决同纹理模型边界区分问题:

方案1:基于对象ID的后处理描边(最适配不规则网格)

原理:不依赖模型本身的边、法线信息,而是给每个独立模型分配唯一ID值,单独渲染一张ID缓存图,后处理阶段比对相邻像素的ID值,只要ID不同就绘制描边,完全不受Sketchup导出的不规整网格影响。
实现步骤:

  • 给所有需要区分的模型分配唯一整型ID,存在mesh.userData.objectId中
  • 自定义渲染通道,将每个模型的ID转为纯色渲染到离屏缓冲
  • 后处理阶段遍历缓冲纹理,相邻像素ID不同的位置叠加描边颜色
    核心代码片段:
// 1. 初始化给每个模型分配唯一ID
let idCounter = 1;
scene.traverse((obj) => {
  if (obj.isMesh) {
    obj.userData.objectId = idCounter++;
  }
})

// 2. 自定义ID渲染材质
const idMaterial = new THREE.ShaderMaterial({
  vertexShader: `
    void main() {
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform float objectId;
    void main() {
      // 将ID转为RGB值存储
      float r = floor(objectId / 65536.0) / 255.0;
      float g = floor(mod(objectId, 65536.0) / 256.0) / 255.0;
      float b = mod(objectId, 256.0) / 255.0;
      gl_FragColor = vec4(r, g, b, 1.0);
    }
  `,
  uniforms: { objectId: { value: 0 } }
})

// 3. 渲染ID缓冲时动态替换材质
const idRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
function renderIdBuffer() {
  scene.traverse((obj) => {
    if (obj.isMesh) {
      obj.userData.originalMaterial = obj.material;
      idMaterial.uniforms.objectId.value = obj.userData.objectId;
      obj.material = idMaterial;
    }
  })
  renderer.render(scene, camera, idRenderTarget);
  // 恢复原材质
  scene.traverse((obj) => {
    if (obj.isMesh) {
      obj.material = obj.userData.originalMaterial;
    }
  })
}

适用场景:需要清晰、统一宽度的模型外边界描边,不受模型拓扑质量影响


方案2:模型膨胀描边(轻量无后处理)

原理:给每个模型复制一个纯色描边副本,开启背面渲染,沿法线方向/整体略微放大,叠在原模型底层,仅露出边缘部分作为描边,实现简单,性能开销极低。
核心代码片段:

scene.traverse((obj) => {
  if (obj.isMesh && !obj.userData.outlineMesh) {
    const outlineGeo = obj.geometry.clone();
    const outlineMat = new THREE.MeshBasicMaterial({
      color: 0x333333, // 描边颜色
      side: THREE.BackSide, // 只渲染背面,避免遮挡原模型
      depthWrite: false
    });
    const outlineMesh = new THREE.Mesh(outlineGeo, outlineMat);
    // 整体放大1.01倍,也可以通过着色器沿法线偏移适配缩放不均匀的模型
    outlineMesh.scale.setScalar(1.01);
    obj.add(outlineMesh);
    obj.userData.outlineMesh = outlineMesh;
  }
})

适用场景:项目复杂度低,不想引入后处理管线,描边精度要求不高


方案3:纹理淡色叠加(无描边低干扰)

原理:如果不需要明显描边,仅需要区分相邻同纹理模型,可以给每个模型的材质叠加一个极低饱和度的偏色,人眼几乎感知不到单模型的色彩变化,但相邻模型的边界会因为极淡的色差自然区分,完全不破坏原有纹理的显示效果。
核心代码片段:

// 预设一组低饱和度的淡色可选
const tintColors = [
  new THREE.Color(0xffffff), // 原色
  new THREE.Color(0xf8f8ff), // 极淡蓝
  new THREE.Color(0xfff8f8), // 极淡红
  new THREE.Color(0xf8fff8), // 极淡绿
  new THREE.Color(0xfffbf8), // 极淡橙
]
let tintIndex = 0;
scene.traverse((obj) => {
  if (obj.isMesh) {
    // 多材质情况循环处理
    const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
    materials.forEach(mat => {
      mat.color.copy(tintColors[tintIndex % tintColors.length]);
      // 开启Renderer.outputColorSpace = SRGBColorSpace时需要加这行做颜色空间转换
      mat.color.convertSRGBToLinear();
    })
    tintIndex++;
  }
})

适用场景:不想添加描边破坏显示风格,仅需要区分相邻模型边界

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 14:06:04