Hi all.
I’ve been struggling with the performance of my Three.js scene for a number of months. Tweaking here and there when I can but I can’t seem to improve the performance.
I’ve read somehwere that I could be rebuilding my geometry once per frame but if I am, I’m not sure how to rectify it.
The code is spread across several layers but the bulk of it is here:
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, scene, renderer, plaquetext1, plaquetext2;
var globalloader;
var globalscene;
var plaque_mesh;
var plaquetext_mesh;
var frame_mesh;
var framebacking_mesh;
var glob_materialframe;
var glob_materialplaque;
init();
//animate();
function init() {
container = document.createElement( 'div' );
scene = new THREE.Scene();
globalscene = scene;
scene.background = new THREE.Color( 0xFFFFFF );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 500 );
// Z is up for objects on bed.
camera.up.set( 0, 0, 1 );
camera.position.set( 0, -10, 10 );
camera.add( new THREE.PointLight( 0xffffff, 0.8 ) );
scene.add( camera );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
renderer.domElement.setAttribute("id", "display");
document.body.appendChild( renderer.domElement );
// Loader
var loader = new THREE.STLLoader();
globalloader = loader;
var materialframe = new THREE.MeshStandardMaterial(
{
color: 0x222222,
//specular: 0x222222,
metalness: 75,
roughness:75
}
);
glob_materialframe = materialframe;
var materialglass = new THREE.MeshPhongMaterial(
{
color: 0xffffff,
specular: 0x222222,
shininess: 200,
opacity: 0.1,
transparent: true,
reflectivity: true,
metalness:0.1
}
);
var materialplaque = new THREE.MeshBasicMaterial(
{
color: 0xd4af37,
//specular: 0x222222,
//shininess: 200,
//opacity: 1,
//transparent: false,
//reflectivity: true,
//metalness:.7
}
);
glob_materialplaque = materialplaque;
var bottomColor = new THREE.Color( 0xFF0000 );
var topColor = new THREE.Color( 0x00FF00);
//Create a frame shape..
function frame() {
var frameshape = new THREE.Shape();
frameshape.moveTo(-4, -4);
frameshape.lineTo( 4, -4);
frameshape.lineTo( 4, 4);
frameshape.lineTo(-4, 4);
//..with a hole:
var hole = new THREE.Path();
hole.moveTo(-3.25, -3.25);
hole.lineTo( 3.25, -3.25);
hole.lineTo( 3.25, 3.25);
hole.lineTo(-3.25, 3.25);
frameshape.holes.push(hole);
var frameback = new THREE.Shape();
frameback.moveTo(-4, -4);
frameback.lineTo( 4, -4);
frameback.lineTo( 4, 4);
frameback.lineTo(-4, 4);
//Extrude the shape into a geometry, and create a mesh from it:
var extrudeSettings = {
steps: 1,
depth: 1.5,
bevelEnabled: false,
};
var extrudeSettingsFrameBack = {
steps: 1,
depth: 0.05,
bevelEnabled: false,
};
var geom = new THREE.ExtrudeGeometry(frameshape, extrudeSettings);
var geomback = new THREE.ExtrudeGeometry(frameback, extrudeSettingsFrameBack);
var frame = new THREE.Mesh(geom, new THREE.MeshPhongMaterial({ color: 0x222222 }));
var framebacking = new THREE.Mesh(geomback, new THREE.MeshPhongMaterial({ color: 0x222222 }));
frame.scale.set( 0.6, 0.6, 0.6 );
framebacking.scale.set( 0.6, 0.6, 0.6 );
frame.castShadow = true;
framebacking.castShadow = true;
frame.receiveShadow = true;
framebacking.receiveShadow = true;
scene.add(frame);
scene.add(framebacking);
render();
frame_mesh = frame;
framebacking_mesh = framebacking;
}
frame();
// PLAQUE
function plaque() {
loader.load( 'http://morgsol.ddns.net:8000/wp-includes/models/stl/plaque.stl', function ( geometry ) {
var meshMaterial = materialplaque;
if (geometry.hasColors) {
meshMaterial = new THREE.MeshBasicMaterial({ color: 0xd4af37 });
}
var plaque = new THREE.Mesh( geometry, meshMaterial );
plaque.position.set( -0.75, -2.2, 0.915 );
plaque.scale.set( 0.02, 0.02, 0.02 );
plaque.castShadow = false;
plaque.receiveShadow = false;
scene.add(plaque);
render();
plaque_mesh = plaque;
} );
}
plaque();
previous_mesh = null;
function model() {
loader.load( 'http://morgsol.ddns.net:8000/wp-includes/models/stl/coedybrenin.stl', function ( geometry ) {
geometry.computeBoundingBox();
geometry.rotateX( -Math.PI / 2 );
var material = new THREE.MeshPhongMaterial({ color: bottomColor });
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set( 0, 0, 0.1 );
mesh.rotateX( Math.PI / 2 );
mesh.receiveShadow = true;
mesh.geometry.center();
mesh.position.z = 0.3;
mesh.scale.set( 0.02, 0.02, 0.02 );
scene.add(mesh);
previous_mesh = mesh;
} ) ;
}
model();
function removeFrame() {
scene.remove(frame);
scene.remove(frame_backing);
// corect way to remove a mesh
frame.geometry.dispose();
frame.material.dispose();
frame = undefined;
framebacking.geometry.dispose();
framebacking.material.dispose();
framebacking = undefined;
}
// TEXT
function plaquetext() {
var loader = new THREE.FontLoader();
loader.load( 'http://morgsol.ddns.net:8000/wp-includes/fonts/helvetiker_regular.typeface.json', function ( font ) {
var xMid, text;
var color = 0xFFFFFF;
var matDark = new THREE.LineBasicMaterial( {
color: color,
side: THREE.DoubleSide
} );
var matLite = new THREE.MeshBasicMaterial( {
color: color,
transparent: true,
opacity: 1,
side: THREE.DoubleSide
} );
var messageLine1 = "Plaque Text Line 1\nLine 2 Plaque Text";
var shapes = font.generateShapes( messageLine1, 0.08 );
var geometry = new THREE.ShapeBufferGeometry( shapes );
geometry.computeBoundingBox();
xMid = - 0.5 * ( geometry.boundingBox.max.x - geometry.boundingBox.min.x );
geometry.translate( xMid, 0, 0 );
text = new THREE.Mesh( geometry, matLite );
text.position.z = 0.9375;
text.position.y = -2;
scene.add( text );
plaquetext_mesh = text;
} ); //end load function END TEXT
}
plaquetext();
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render );
controls.target.set( 0, 1.2, 2 );
controls.update();
window.addEventListener( 'resize', onWindowResize, false );
window.addEventListener( 'change', render );
/* Set the width of the side navigation to 250px and the left margin of the page content to 250px */
function openNav() {
document.getElementById("Div2").style.width = "250px";
//document.getElementById("Div1").style.marginLeft = "-250px";
}
/* Set the width of the side navigation to 0 and the left margin of the page content to 0 */
function closeNav() {
document.getElementById("Div2").style.width = "0";
//document.getElementById("Div1").style.marginLeft = "0";
}
// END INIT
}
/* Set the width of the side navigation to 0 and the left margin of the page content to 0 */
function closeNav() {
document.getElementById("Div2").style.width = "0";
//document.getElementById("Div1").style.marginLeft = "0";
}
////////// FUNCTIONS //////////
function addShadowedLight( x, y, z, color, intensity ) {
var directionalLight = new THREE.DirectionalLight( color, intensity );
directionalLight.position.set( x, y, z );
scene.add( directionalLight );
directionalLight.castShadow = true;
var d = 1;
directionalLight.shadow.camera.left = -d;
directionalLight.shadow.camera.right = d;
directionalLight.shadow.camera.top = d;
directionalLight.shadow.camera.bottom = -d;
directionalLight.shadow.camera.near = 1;
directionalLight.shadow.camera.far = 4;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.bias = -0.002;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
render();
}
function plaqueChange(val) {
alert("The input value has changed. The new value is: " + val);
window.location.reload() ;
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
//Note: this goes inside render loop
requestAnimationFrame(render);
renderer.render( scene, camera );
// shadowMapViewer.render(render.renderer); // IF this is included, the resize doesn't work
}
The end result (unfinished) is a kind of product configurator
The primary model in the scene (an STL file loaded using the STLLoader) has a vertex count of 4,668 and has 1,556 faces. Not really that much compared to much more complex scenes I’ve seen with >50000 individual objects. The plaque is really simple having just 36 vertices and only 12 faces. Finally the frame shape itself is just an extruded shape so should in theory should perform very well.
Any tips on how I can improve the performance of my scene? It’s pretty much unusable on mobile devices.