threejs中bufferGeometry合并几何体实现

效果图

在项目中升级threejs版本,原版本中用到了Geometry.merge()方法。升级后的threejs删除了Geometry相关类,转而使用BufferGeometry实现生成几何体。BufferGeometry中的merge()方法使用后,并未将多个几何体进行合并,故自实现了合并多个几何体的功能。

本文中的合并几何体函数只对position、normal、uv、groups进行了合并,合并的几何体为shape拉伸的多个几何体,且为表面和侧立面分别使用不同的material。

以下为实现合并多个几何体方法代码(方法参照javascript - How to merge two BufferGeometries in one BufferGeometry in Three.JS? - Stack Overflow):

  const mergeBufferGeometry = function (objects) {
    const sumPosArr = new Array();
    const sumNormArr = new Array();
    const sumUvArr = new Array();

    const modelGeometry = new THREE.BufferGeometry();

    let sumPosCursor = 0;
    let sumNormCursor = 0;
    let sumUvCursor = 0;

    let startGroupCount = 0;
    let lastGroupCount = 0;

    for (let a = 0; a < objects.length; a++ )
    {
      const posAttArr = objects[a].geometry.getAttribute('position').array;

      for (let b = 0; b < posAttArr.length; b++)
      {
        sumPosArr[b + sumPosCursor] = posAttArr[b];
      }

      sumPosCursor += posAttArr.length;


      const numAttArr = objects[a].geometry.getAttribute('normal').array;

      for (let b = 0; b < numAttArr.length; b++)
      {
        sumNormArr[b + sumNormCursor] = numAttArr[b];
      }

      sumNormCursor += numAttArr.length;


      const uvAttArr = objects[a].geometry.getAttribute('uv').array;

      for (let b = 0; b < uvAttArr.length; b++)
      {
        sumUvArr[b + sumUvCursor] = uvAttArr[b];
      }

      sumUvCursor += uvAttArr.length;

      const groupArr = objects[a].geometry.groups;

      for (let b = 0; b < groupArr.length; b++)
      {
        startGroupCount = lastGroupCount
        modelGeometry.addGroup(startGroupCount, groupArr[b].count, groupArr[b].materialIndex)
        lastGroupCount = startGroupCount + groupArr[b].count
      }
    }

    modelGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sumPosArr, 3 ));
    sumNormArr.length && modelGeometry.setAttribute('normal', new THREE.Float32BufferAttribute(sumNormArr, 3 ));
    sumUvArr.length &&  modelGeometry.setAttribute('uv', new THREE.Float32BufferAttribute(sumUvArr, 2 ));

    return modelGeometry
  }

1)objects 传参:需要合并的几何体数组,mesh数组

2)position 合并:将 objects 数组中几何体的 position 合并至一个数组中,最后将全部 position 添加至新创建的 modelGeometry 中并返回

3)normal 合并:将 objects 数组中几何体的 normal 合并至一个数组中,最后将全部 normal 添加至新创建的 modelGeometry 中并返回

4)uv 合并:将 objects 数组中几何体的 uv 合并至一个数组中,最后将全部 uv 添加至新创建的 modelGeometry 中并返回

5)groups 合并:单个 groups:{ start: Integer, count: Integer, materialIndex: Integer },合并 groups 需要重新计算 start 的值(注意此处的 count 个数并不是 position 个数),下一个 start 的值总是等于上一个 start + count 。本文中 groups 来源为创建ExtrudeGeometry几何体时,传入了表面和侧立面的 material。

生成地图3D图形代码部分

  drawChinaMap() {
    const chinaGroup = new THREE.Group()

    const bumpMap = new THREE.TextureLoader().load(require('./assets/img/normal1.jpg'), texture => {
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.offset.set( 0, 0.5 )
      texture.repeat.set( 0.01, 0.01 )
    })

    chinaGeometry.features.forEach(chinaItem => {
      let oneCityLineGroup = new THREE.Group()
      let oneCityLineArr = []
      let oneCityShape
      let oneCityShapeArr = []
      let material1 = new THREE.ShaderMaterial({
        uniforms: Shader1.uniforms,
        vertexShader: Shader1.vertexShader,
        fragmentShader: Shader1.fragmentShader,
        side: THREE.DoubleSide,
        transparent: true,
        // blending: THREE.AdditiveBlending,
      })

      let material2 = new THREE.MeshStandardMaterial({
        // color: '#7293b5',
        bumpMap: bumpMap,
        // map: bumpMap,
        opacity: 0.8,
        bumpScale: 10,
        roughness: 1,
        metalness: 0.0,
      })

      chinaItem.geometry.coordinates.forEach(chinaChildItem => {
        if (chinaItem.geometry.type === "MultiPolygon") {
          chinaChildItem.forEach((item) => {
            const oneCityShape = this.drawShape(item, [material2, material1])
            oneCityShapeArr.push(oneCityShape)
          })
        } else {
          const oneCityShape = this.drawShape(chinaChildItem, [material2, material1])
          oneCityShapeArr.push(oneCityShape)
        }
      })
      const newGeometry = this.mergeBufferGeometry(oneCityShapeArr)
      oneCityShape = new THREE.Mesh(newGeometry, [material2, material1])
      oneCityShape.name = chinaItem.properties.name
      this.outlinePass.selectedObjects.push(oneCityShape)

      chinaGroup.add(oneCityShape)

    })
    chinaGroup.rotateX(-Math.PI/2)
    chinaGroup.scale.set(0.0015, 0.0015, 0.0015)
    this.scene.add(chinaGroup)
  }

拉伸shape任意几何体方法如下:

  drawShape(position, material) {
    const pointArr = []
    const shape = new THREE.Shape()
    const extrudeSettings = {
      steps: 2,
      depth: 100000,
      curveSegments: 128,
      bevelEnabled: true,
      bevelThickness: 1,
      bevelSize: 1,
      bevelOffset: 10,
      bevelSegments: 1
    };

    position.forEach(point => {
      const coord = geographicToVector2(point)
      pointArr.push([coord[0]-center[0], coord[1] - center[1]]);
    })
    pointArr.forEach((p, i) => {
      i === 0 ? shape.moveTo(p[0], p[1]) : shape.lineTo(p[0], p[1])
    })
    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)

    return new THREE.Mesh(geometry, material)
  }

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
要在 Three.js 实现几何体挖洞,你可以使用 Three.js 提供的布尔运算工具来将一个几何体从另一个几何体减去。 下面是一个示例代码,展示了如何在 Three.js 实现几何体挖洞: ```javascript // 创建场景 var scene = new THREE.Scene(); // 创建相机 var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 5; // 创建渲染器 var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 创建外部几何体 var outerBoxGeometry = new THREE.BoxGeometry(3, 3, 3); var outerBoxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 设置外部几何体的颜色为绿色 var outerBox = new THREE.Mesh(outerBoxGeometry, outerBoxMaterial); // 创建内部几何体 var innerBoxGeometry = new THREE.BoxGeometry(1, 1, 1); var innerBoxMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // 设置内部几何体的颜色为红色 var innerBox = new THREE.Mesh(innerBoxGeometry, innerBoxMaterial); // 创建一个几何体组合,包括外部几何体和内部几何体 var group = new THREE.Group(); group.add(outerBox); group.add(innerBox); // 使用 Three.js 的布尔运算工具减去内部几何体 var subtractGeometry = new ThreeBSP(outerBoxGeometry).subtract(new ThreeBSP(innerBoxGeometry)); var resultGeometry = subtractGeometry.toMesh(); resultGeometry.material = outerBoxMaterial; // 将结果几何体替换原来的外部几何体 scene.add(resultGeometry); // 渲染场景 function render() { requestAnimationFrame(render); group.rotation.x += 0.01; group.rotation.y += 0.01; renderer.render(scene, camera); } render(); ``` 上述代码创建了一个绿色的外部几何体和一个红色的内部几何体,并使用布尔运算工具将内部几何体从外部几何体减去,实现了挖洞效果。你可以根据需要调整外部几何体和内部几何体的形状、大小和材质等参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值