簡介
多數情況下使用組可以很容易地操縱和管理大量網格。但是當對象的數量非常多時,性能就會成為一個瓶頸。使用組,每個對象還是獨立的,仍然需要對它們分别進行處理和渲染。通過
THREE.Geometry.merge()
函數,你可以将多個幾何體合并起來建立一個聯合體。
檢視案例:http://www.wjceo.com/blog/threejs/2018-03-14/123.html
當我們使用普通組的情況,繪制20000個立方體,幀率在15幀左右,如果我們選擇合并以後,再繪制兩萬,就會發現,我們可以輕松的渲染20000個立方體,而且沒有性能的損失。合并的代碼如下:
//合并模型,則使用merge方法合并
var geometry = new THREE.Geometry();
//merge方法将兩個幾何體對象或者Object3D裡面的幾何體對象合并,(使用對象的變換)将幾何體的頂點,面,UV分别合并.
//THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.
for(var i=; i<gui.numberOfObjects; i++){
var cube = addCube();
cube.updateMatrix();
geometry.merge(cube.geometry, cube.matrix);
}
scene.add(new THREE.Mesh(geometry, cubeMaterial));
THREE.GeometryUtils.merge()
已經将此方法移動到了
THREE.Geometry
對象的上面了,我們使用
addCube
方法進行立方體的建立,為了確定能正确的定位和旋轉合并的
THREE.Geometry
對象,我們不僅向
merge
函數提供
THREE.Geometry
對象,還提供該對象的變換矩陣。當我們将此矩陣添加到
merge
函數後,那麼合并的方塊将被正确定位。
群組的優缺點對比
- 缺點:組能夠對每個單獨的個體進行操作,而合并網格後則失去對每個對象的單獨控制。想要移動、旋轉或縮放某個方塊是不可能的。
- 優點:性能不會有損失。因為将所有的的網格合并成為了一個,性能将大大的增加。
- 如果需要建立大型的、複雜的幾何體。我們還可以從外部資源中建立、加載幾何體。
案例代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
html, body {
margin: ;
height: %;
}
canvas {
display: block;
}
</style>
</head>
<body onload="draw();">
</body>
<script src="/lib/three.js"></script>
<script src="/lib/js/controls/OrbitControls.js"></script>
<script src="/lib/js/libs/stats.min.js"></script>
<script src="/lib/js/libs/dat.gui.min.js"></script>
<script>
var renderer;
function initRender() {
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
//告訴渲染器需要陰影效果
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 預設的是,沒有設定的這個清晰 THREE.PCFShadowMap
document.body.appendChild(renderer.domElement);
}
var camera;
function initCamera() {
camera = new THREE.PerspectiveCamera(, window.innerWidth/window.innerHeight, , );
camera.position.set(, , );
camera.lookAt(new THREE.Vector3(,,));
}
var scene;
function initScene() {
scene = new THREE.Scene();
}
//初始化dat.GUI簡化試驗流程
var gui;
function initGui() {
//聲明一個儲存需求修改的相關資料的對象
gui = {
numberOfObjects:, //目前場景中模型的個數
combined:false, //是否合并模型
redraw:function () {
//删除場景内現有的立方體
var arr = [];
scene.traverse(function (e) {
if (e instanceof THREE.Mesh) arr.push(e);
});
arr.forEach(function (value) {
scene.remove(value);
});
//重新生成立方體
if(gui.combined){
//合并模型,則使用merge方法合并
var geometry = new THREE.Geometry();
//merge方法将兩個幾何體對象或者Object3D裡面的幾何體對象合并,(使用對象的變換)将幾何體的頂點,面,UV分别合并.
//THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.
for(var i=; i<gui.numberOfObjects; i++){
var cube = addCube();
cube.updateMatrix();
geometry.merge(cube.geometry, cube.matrix);
}
scene.add(new THREE.Mesh(geometry, cubeMaterial));
}
else{
//不合并模型,則直接将模型放入
for(var i=; i<gui.numberOfObjects; i++){
scene.add(addCube());
}
}
}
};
var datGui = new dat.GUI();
//将設定屬性添加到gui當中,gui.add(對象,屬性,最小值,最大值)
//添加旋轉功能
datGui.add(gui, "numberOfObjects", , );
datGui.add(gui, "combined");
datGui.add(gui, "redraw");
gui.redraw();
}
//建立立方體的方法
var cubeMaterial = new THREE.MeshLambertMaterial({color: , transparent: true, opacity: });
function addCube() {
var cubeSize = ;
var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.position.x = -+Math.round(Math.random()*);
cube.position.y = -+Math.round(Math.random()*);
cube.position.z = -+Math.round(Math.random()*);
return cube;
}
var light;
function initLight() {
scene.add(new THREE.AmbientLight());
light = new THREE.DirectionalLight();
light.position.set(,,);
//告訴平行光需要開啟陰影投射
light.castShadow = true;
scene.add(light);
}
var sphere,cube;
function initModel() {
//輔助工具
var helper = new THREE.AxisHelper();
scene.add(helper);
return;
//模型組
group = new THREE.Object3D();
scene.add(group);
//球
var sphereGeometry = new THREE.SphereGeometry(,,);
var sphereMaterial = new THREE.MeshLambertMaterial({color:});
sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = -;
sphere.position.y = ;
//告訴球需要投射陰影
sphere.castShadow = true;
group.add(sphere);
//立方體
var cubeGeometry = new THREE.CubeGeometry(,,);
var cubeMaterial = new THREE.MeshLambertMaterial({color:});
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = ;
cube.position.y = ;
cube.position.z = -;
//告訴立方體需要投射陰影
cube.castShadow = true;
group.add(cube);
//底部平面
var planeGeometry = new THREE.PlaneGeometry(,);
var planeMaterial = new THREE.MeshStandardMaterial({color:});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = - * Math.PI;
plane.position.y = -;
//告訴底部平面需要接收陰影
plane.receiveShadow = true;
scene.add(plane);
}
//初始化性能插件
var stats;
function initStats() {
stats = new Stats();
document.body.appendChild(stats.dom);
}
//使用者互動插件 滑鼠左鍵按住旋轉,右鍵按住平移,滾輪縮放
var controls;
function initControls() {
controls = new THREE.OrbitControls( camera, renderer.domElement );
// 如果使用animate方法時,将此函數删除
//controls.addEventListener( 'change', render );
// 使動畫循環使用時阻尼或自轉 意思是否有慣性
controls.enableDamping = true;
//動态阻尼系數 就是滑鼠拖拽旋轉靈敏度
//controls.dampingFactor = 0.25;
//是否可以縮放
controls.enableZoom = true;
//是否自動旋轉
controls.autoRotate = true;
controls.autoRotateSpeed = ;
//設定相機距離原點的最近距離
controls.minDistance = ;
//設定相機距離原點的最遠距離
controls.maxDistance = ;
//是否開啟右鍵拖拽
controls.enablePan = true;
}
var step = ; //模型旋轉的速度
function render() {
renderer.render( scene, camera );
}
//視窗變動觸發的函數
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
render();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
//更新控制器
render();
//更新性能插件
stats.update();
controls.update();
requestAnimationFrame(animate);
}
function draw() {
initRender();
initScene();
initCamera();
initLight();
initModel();
initGui();
initControls();
initStats();
animate();
window.onresize = onWindowResize;
}
</script>
</html>