当前位置: 代码迷 >> 综合 >> 使用vue学习three.js之创建、加载高级几何体-通过THREE.Geometry.merge()函数合并网格
  详细解决方案

使用vue学习three.js之创建、加载高级几何体-通过THREE.Geometry.merge()函数合并网格

热度:110   发布时间:2023-09-30 22:39:03.0

1.demo效果

使用vue学习three.js之创建、加载高级几何体-通过THREE.Geometry.merge()函数合并网格

如上图,该demo支持以下功能:

可以通过objNum属性设置要创建的小立方体的个数。
可以通过勾选combined属性来设置是否启用THREE.Geometry.merge()函数合并立方体

2.涉及知识点

2.1 为什么合并网格

一般情况下可以使用组来操纵和管理大量网格。但是当对象的数量非常庞大时,性能就会成为一个瓶颈,因为在组里每个对象还是独立的,需要分别对它们进行渲染和处理,所以数量上来以后性能就会受到很大影响。这个时候three.js提供了一个方法可以将网格合并,来达到提升性能的目的

2.2 THREE.Geometry.merge()函数

现在的网格合并函数merge已经合成到THREE.Geometry对象上,我们使用合并函数可以直接通过Geometry对象调用。THREE.Geometry.merge()函数的可以将一个或多个几何体合并成一个几何体
以下是使用示例:

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 网格合并产生的影响

合并后,若干个网格对象被合并成一个,性能大幅提升
与使用组或未合并相比较,合并后不能对每个对象进行单独的操作

3.实现要点

3.1 创建随机位置的小立方体

合并网格需要多个小立方体的网格对象,我们首先要实现一个创建随机位置立方体的方法,具体如下:

addCube () {const cubeSize = 1.0const 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 = truecube.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 合并网格对象

上面的步骤我们实现了一个创建随机位置方块的方法,这里我们使用for循环通过merge方法将创建的小方块依次合并到新的geometry几何对象,并使用它创建新的网格对象,最后添加到场景

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 重绘网格对象

当我们通过objNum属性调整要生成的小方块或勾选combined属性来调整是否合并对象时,页面需要重新绘制,这上需要先把上一次创建的网格对象先全部删掉,然后重新创建,具体如下:

redraw () {const toRemove = []this.scene.traverse(e => {if (e instanceof THREE.Mesh) toRemove.push(e)})toRemove.forEach(e => {this.scene.remove(e)})this.createCubes()
}
<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 = truethis.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.0const 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 = truecube.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.005this.camera.position.x = Math.sin(this.rotation) * 50this.camera.position.z = Math.cos(this.rotation) * 50this.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>

原文链接:https://blog.csdn.net/qw8704149/article/details/112341295

  相关解决方案