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
相关产品推荐
相关产品推荐

