本篇介紹three.js
性能優化的若干方法。(個人拙見)
three.js性能優化
盡量重用Material和Geometry
這里以Material和Geometry為例(使用比較頻繁)
for (var i = 0; i < 100; i++) {
var material = new THREE.MeshBasicMaterial();
var geometry = new THREE.BoxGeometry(10, 10, 10);
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
改為
var material = new THREE.MeshBasicMaterial();
var geometry = new THREE.BoxGeometry(10, 10, 10);
for (var i = 0; i < 100; i++) {
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
謹慎的在render()中操作
一般FPS為60也就意味着一秒會執行60次如果render()中有有實例化或是賦值操作很容易會崩潰。
如下:
function render() {
material.map = canvasMap;
material.map.needsUpdate = true;
}
選擇合適的對象
THREE.ParticleSystem
(粒子系統)代替THREE.Particle
(粒子)
for (var x = -5; x < 5; i++) {
for (var y = -5; y < 5; y++) {
var particle = new THREE.Particle(material);
particle.position.set(x * 10, y * 10, 0);
scene.add(particle);
}
}
代替
var geometry = new THREE.Geometry();
var material = new THREE.ParticleBasicMaterial();
for (var x = -5; x < 5; i++) {
for (var y = -5; y < 5; y++) {
var particle = new THREE.Vector3(x * 10, y * 10, 0);
geometry.vertices.push(particle);
}
}
var system = new THREE.ParticleSystem(geometry,material);
scene.add(system);
- 要操作一組對象時使用
THREE.Object3D
var contain = new THREE.Object3D();
var material = new THREE.MeshBasicMaterial();
var geometry = new THREE.BoxGeometry(10, 10, 10);
for (var i = 0; i < 100; i++) {
var mesh = new THREE.Mesh(geometry, material);
contain.add(mesh);
}
contain.rotation.x = Math.PI/3;
- 網格合並
THREE.GeometryUtils.merge
R60
var geometry = new THREE.BoxGeometry(5, 5, 5);
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
for (var i = 0; i < 100; i++) {
THREE.GeometryUtils.merge(geometry, new THREE.BoxGeometry(5, 5, 5));
}
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
或是一下方式(geometry有位置信息)
var geometry = new THREE.BoxGeometry(5, 5, 5);
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
var mesh = new THREE.Mesh(geometry,material);
mesh.position.set(10,10,10);
var mGeo = new THREE.BoxGeometry(10, 2, 10);
THREE.GeometryUtils.merge(mGeo, mesh);
scene.add(new THREE.Mesh(mGeo, material));
R80 THREE.GeometryUtils.merge()
change to geometry.merge()
var geometry = new THREE.BoxGeometry(2, 4, 2);
var mGeo = new THREE.BoxGeometry(5, 5, 5);
var matrix = new THREE.Matrix4();
for(var i=0;i<100;i++){
matrix.setPosition(new THREE.Vector3(Math.random()*10,0,Math.random()*10));
geometry.merge(mGeo, matrix);
}
var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({color: 0xff0000}));
scene.add(mesh);
使用Web Workers
web worker
是運行在后台的 JavaScript,它獨立於其他腳本,不會影響頁面的性能
我們會發現Physijs
物理庫就是使用這種方式來保證頁面的性能。
使用之前你可能需要是否支持web worker
if(typeof(Worker)!=="undefined"){}else { }
我們將web worker
運行腳本放在一個js文件中。
three_workers.js:
// 添加監聽事件,從主線程接收數據
self.addEventListener('message', function(event) {
// 向主線程發送數據
self.postMessage({
some_data: '',
more_data: ''
})
})
主線程.js:
//新建一個 `Web Workers`
var worker = new Worker('three_workers.js');
// 添加監聽事件,獲取`Web Workers`傳回數據
worker.addEventListener('message', function (event) {
var data = event.data;
});
// 向`Web Workers`發送數據
worker.postMessage({
some_data: '',
more_data: ''
});
可以參照:
https://threejs.org/examples/?q=sand#raytracing_sandbox
function timedChunk(items, process, context, callback){
var todo = items.concat(); //create a clone of the original
setTimeout(function(){
var start = +new Date();
do {
process.call(context, todo.shift());
} while (todo.length > 0 && (+new Date() - start < 50));
if (todo.length > 0){
setTimeout(arguments.callee, 25);
} else {
callback(items);
}
}, 25);
}
分時加載
可以參照:
https://www.nczonline.net/blog/2009/08/11/timed-array-processing-in-javascript/
分時加載算法(大數組)
調查顯示100ms內的響應能讓用戶感覺非常流暢。50ms是 Nicholas
針對 JavaScript
得出的最佳經驗值。
setTimeout 延時25ms,25ms 保證主流瀏覽器都順暢。
可以使用類似的方法來優化three.js程序。
//Copyright 2009 Nicholas C. Zakas. All rights reserved.
//MIT Licensed
function timedChunk(items, process, context, callback){
var todo = items.concat();
setTimeout(function(){
var start = +new Date();
do {
process.call(context, todo.shift());
} while (todo.length > 0 && (+new Date() - start < 50));
if (todo.length > 0){
setTimeout(arguments.callee, 25);
} else {
callback(items);
}
}, 25);
};
程序分析見
使用自定義着色器
以后會有一篇專門講着色器