Use vue to learn three.js to create and load advanced geometry-merge meshes through the THREE.Geometry.merge() function

1.demo effect

Insert picture description here
As shown above, the demo supports the following functions:

  1. The number of small cubes to be created can be set through the objNum property.
  2. You can set whether to enable the THREE.Geometry.merge() function to merge cubes by checking the combined attribute

2. Involving knowledge points

2.1 Why merge grids

In general, you can use groups to manipulate and manage a large number of grids. But when the number of objects is very large, performance will become a bottleneck, because each object in the group is independent and needs to be rendered and processed separately, so the performance will be greatly affected when the number is up. At this time three.js provides a method to merge grids to achieve the purpose of improving performance

2.2 THREE.Geometry.merge() function

Now the mesh merge function merge has been synthesized on the THREE.Geometryobject, and we can use the merge function to call it directly through the Geometry object. THREE.Geometry.merge()The function can merge one or more geometric bodies into one geometric body. The
following is an example of its use:


const cubeMaterial = new THREE.MeshNormalMaterial({
    
    
  transparent: true,
  opacity: 0.5
})
//创建方块1
const cubeGeometry1 = new THREE.BoxGeometry(10, 10, 10)
const cube1 = new THREE.Mesh(cubeGeometry1, cubeMaterial)
//创建方块2
const cubeGeometry2 = new THREE.BoxGeometry(20, 20, 20)
const cube2 = new THREE.Mesh(cubeGeometry, cubeMaterial)

const geometry = new THREE.Geometry()

//将方块1合并到geometry 
cube1.updateMatrix()
geometry.merge(cube1.geometry, cube1.matrix)

//将方块2合并到geometry
cube2.updateMatrix()
geometry.merge(cube2.geometry, cube2.matrix)

//用合并后的几何对象创建网格
const mergeMesh = new THREE.Mesh(geometry, cubeMaterial)

//将使用合并方式创建的网格对象添加到场景
scene.add(mergeMesh )

2.3 The impact of grid merging

  1. After merging, several mesh objects are merged into one, and the performance is greatly improved
  2. Compared with the use of groups or unmerged, separate operations cannot be performed on each object after merging

3. Implementation points

3.1 Create small cubes with random positions

Combining grids requires multiple grid objects of small cubes. We must first implement a method for creating cubes with random positions, as follows:

addCube () {
    
    
 const cubeSize = 1.0
 const cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize)
 const cubeMaterial = new THREE.MeshNormalMaterial({
    
    
   transparent: true,
   opacity: 0.5
 })
 const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
 cube.castShadow = true

 cube.position.x = -60 + Math.round(Math.random() * 100)
 cube.position.y = Math.round(Math.random() * 10)
 cube.position.z = -150 + Math.round(Math.random() * 175)

 return cube
}

3.2 Combine mesh objects

In the above steps, we have implemented a method of creating random-position squares. Here we use the for loop to merge the created small squares into the new geometry object in turn through the merge method, and use it to create a new mesh object, and finally add it to the scene

const geometry = new THREE.Geometry()
for (let i = 0; i < this.properties.objNum.value; i++) {
    
    
  const cubeMesh = this.addCube()
  cubeMesh.updateMatrix()
  geometry.merge(cubeMesh.geometry, cubeMesh.matrix)
}
this.scene.add(new THREE.Mesh(geometry, cubeMaterial))

3.3 Redraw the mesh object

When we adjust the small squares to be generated through the objNum attribute or check the combined attribute to adjust whether to merge the objects, the page needs to be redrawn. In this case, we need to delete all the mesh objects created last time, and then recreate them. as follows:

redraw () {
    
    
  const toRemove = []
  this.scene.traverse(e => {
    
    
    if (e instanceof THREE.Mesh) toRemove.push(e)
  })
  toRemove.forEach(e => {
    
    
    this.scene.remove(e)
  })
  this.createCubes()
}

4.demo code

<template>
  <div>
    <div id="container"></div>
    <div class="controls-box">
      <section>
        <el-row>
          <div v-for="(item,key) in properties" :key="key">
            <div v-if="item&&item.name!=undefined">
              <el-col :span="8">
                <span class="vertice-span">{
    
    {
    
    item.name}}</span>
              </el-col>
              <el-col :span="13">
                <el-slider v-model="item.value" :min="item.min" :max="item.max" :step="item.step" :format-tooltip="formatTooltip" @change="redraw"></el-slider>
              </el-col>
              <el-col :span="3">
                <span class="vertice-span">{
    
    {
    
    item.value}}</span>
              </el-col>
            </div>
          </div>
        </el-row>
        <el-row>
          <el-checkbox v-model="properties.combined" @change="redraw">combined</el-checkbox>
        </el-row>
      </section>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three'
import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export default {
    
    
  components: {
    
    },
  data () {
    
    
    return {
    
    
      properties: {
    
    
        objNum: {
    
    
          name: 'objNum',
          value: 300,
          min: 0,
          max: 10000,
          step: 1
        },
        combined: false
      },
      cube: null,
      rotation: 0,
      camera: null,
      scene: null,
      renderer: null,
      controls: null
    }
  },
  mounted () {
    
    
    this.init()
  },
  methods: {
    
    
    formatTooltip (val) {
    
    
      return val
    },
    // 初始化
    init () {
    
    
      this.createScene() // 创建场景
      this.createCubes() // 创建方块
      this.createLight() // 创建光源
      this.createCamera() // 创建相机
      this.createRender() // 创建渲染器
      this.createControls() // 创建控件对象
      this.render() // 渲染
    },
    // 创建场景
    createScene () {
    
    
      this.scene = new THREE.Scene()
    },

    // 创建光源
    createLight () {
    
    
      // 添加聚光灯
      const spotLight = new THREE.SpotLight(0xffffff)
      spotLight.position.set(-40, 60, 20)
      spotLight.castShadow = true
      this.scene.add(spotLight) // 聚光灯添加到场景中
      // 环境光
      const ambientLight = new THREE.AmbientLight(0x0c0c0c)
      this.scene.add(ambientLight)
    },
    // 创建相机
    createCamera () {
    
    
      const element = document.getElementById('container')
      const width = element.clientWidth // 窗口宽度
      const height = element.clientHeight // 窗口高度
      const k = width / height // 窗口宽高比
      // PerspectiveCamera( fov, aspect, near, far )
      this.camera = new THREE.PerspectiveCamera(45, k, 0.1, 1000)

      this.camera.position.set(-30, 40, 30) // 设置相机位置
      this.camera.lookAt(new THREE.Vector3(5, 0, 0)) // 设置相机方向
      this.scene.add(this.camera)
    },
    // 创建渲染器
    createRender () {
    
    
      const element = document.getElementById('container')
      this.renderer = new THREE.WebGLRenderer()
      this.renderer.setSize(element.clientWidth, element.clientHeight) // 设置渲染区域尺寸
      this.renderer.setClearColor(0x3f3f3f, 1) // 设置背景颜色
      element.appendChild(this.renderer.domElement)
    },
    addCube () {
    
    
      const cubeSize = 1.0
      const cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize)
      const cubeMaterial = new THREE.MeshNormalMaterial({
    
    
        transparent: true,
        opacity: 0.5
      })
      const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
      cube.castShadow = true

      cube.position.x = -60 + Math.round(Math.random() * 100)
      cube.position.y = Math.round(Math.random() * 10)
      cube.position.z = -150 + Math.round(Math.random() * 175)

      return cube
    },
    // 创建多个方块
    createCubes () {
    
    
      const cubeMaterial = new THREE.MeshNormalMaterial({
    
    
        transparent: true,
        opacity: 0.5
      })
      if (this.properties.combined) {
    
    
        const geometry = new THREE.Geometry()
        for (let i = 0; i < this.properties.objNum.value; i++) {
    
    
          const cubeMesh = this.addCube()
          cubeMesh.updateMatrix()
          geometry.merge(cubeMesh.geometry, cubeMesh.matrix)
        }
        this.scene.add(new THREE.Mesh(geometry, cubeMaterial))
      } else {
    
    
        for (let i = 0; i < this.properties.objNum.value; i++) {
    
    
          const cube = this.addCube()
          this.scene.add(cube)
        }
      }
    },

    redraw () {
    
    
      const toRemove = []
      this.scene.traverse(e => {
    
    
        if (e instanceof THREE.Mesh) toRemove.push(e)
      })
      toRemove.forEach(e => {
    
    
        this.scene.remove(e)
      })
      this.createCubes()
    },
    animation () {
    
    
      this.rotation += 0.005
      this.camera.position.x = Math.sin(this.rotation) * 50
      this.camera.position.z = Math.cos(this.rotation) * 50
      this.camera.lookAt(this.scene.position)
    },
    render () {
    
    
      this.animation()
      this.renderer.render(this.scene, this.camera)
      requestAnimationFrame(this.render)
    },
    // 创建控件对象
    createControls () {
    
    
      this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    }
  }
}
</script>

<style>
#container {
    
    
  position: absolute;
  width: 100%;
  height: 100%;
}
.controls-box {
    
    
  position: absolute;
  right: 5px;
  top: 5px;
  width: 300px;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #c3c3c3;
}
.vertice-span {
    
    
  line-height: 38px;
  padding: 0 2px 0 10px;
}
</style>

Guess you like

Origin blog.csdn.net/qw8704149/article/details/112341295