Performance with a lot of objects?

Hi,

I made a game with a lot of objects.

What is better :

  • use and reuse the mesh ?
  • create mesh and remove mesh depending on demand ?

(i have 40 enemys with logic functions)

With Phaser, it’s the option 1 but when i try with Three.js, it’s seems that is not a good solution…

Another question : is instanciation better with animated objects ?

Tell me what is better…

1 Like

Reusing is generally best. If the same character appears at different places in the scene you can just reuse its Geometry and Material; that’s more important than reusing the Mesh, because the vertex data and textures don’t have to be uploaded to the GPU again.

If you have lots of objects (i.e. 1000+) you may need to consider using InstancedMesh, but fewer objects it’s not worth the effort probably.

Hi donmccurdy,

Thanks for your tips. I made a little count and i have +/- 4000 meshes.
So instanciation is necessary. I’m a little bit confused about this. I’m reading the examples on Three.js but i don’t understand how to translate this ? How do you do perform this with i.e this simple class ?

// class Smoke.js

import * as THREE from "https://threejs.org/build/three.module.js";
import {
    CSM
} from "https://threejs.org/examples/jsm/csm/CSM.js";

class Smoke {
    constructor(sce, x, y, z) {
        this.mesh = new THREE.Object3D();
        g.smoke = new THREE.SphereGeometry(3, 6, 6);
        m.smoke = new THREE.MeshBasicMaterial({
            color: params.color_cloud,
            transparent: true,
            opacity: .5,
        });

        csm.setupMaterial(m.smoke)
        let smoke = new THREE.Mesh(g.smoke, m.smoke);
        smoke.castShadow = true
        smoke.receiveShadow = true
        this.mesh.add(smoke)
        this.mesh.position.set(x, y, z)
        sce.add(this.mesh)
    }
}

export default Smoke
// in main.js
...

		for (var i = 0; i < 1000; i++) {
			o.smoke[i] = new Smoke(scene, i+10, 25,i+10)
		}

Find by myself

//class Instance
import * as THREE from "https://threejs.org/build/three.module.js";
import {
    CSM
} from "https://threejs.org/examples/jsm/csm/CSM.js";
import {
    TWEEN
} from 'https://unpkg.com/three@0.125.2/examples//jsm/libs/tween.module.min'


class Instance {
    constructor(sce) {
        g.cube = new THREE.BoxGeometry(params.size_player * 6, params.size_player, params.size_player);
        m.cube = new THREE.MeshPhongMaterial({
            color: params.color_cube,
        });
        csm.setupMaterial(m.cube)

        this.obj = new THREE.InstancedMesh(g.cube, m.cube, 5);
        this.obj.name = "Instance";
        this.obj.castShadow = true;
        this.obj.receiveShadow = true;

        sce.add(this.obj)
    }

    animate() {
        this.obj.rotation.z += .01
    }
}

export default Instance
//main.js
import * as THREE from "https://threejs.org/build/three.module.js";
import {
	OrbitControls
}
from "https://threejs.org/examples/jsm/controls/OrbitControls.js";
import {
	CSM
} from "https://threejs.org/examples/jsm/csm/CSM.js";
import {
	TWEEN
} from 'https://unpkg.com/three@0.125.2/examples//jsm/libs/tween.module.min'

let camera, scene, renderer
let matrix;
let mesh;
const amount = 15
const count = Math.pow(amount, 3);
var dummy = [];
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2(1, 1);

const color = new THREE.Color();

init();
animate();

function init() {
	css()
	camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
	camera.position.set(amount, amount, amount);
	camera.lookAt(0, 0, 0);

	scene = new THREE.Scene();
	scene.background = new THREE.Color(0x000000);

	const light1 = new THREE.HemisphereLight(0xffffff, 0x000088);
	light1.position.set(-1, 1.5, 1);
	scene.add(light1);

	const light2 = new THREE.HemisphereLight(0xffffff, 0x880000, 0.5);
	light2.position.set(-1, -1.5, -1);
	scene.add(light2);

	const geometry = new THREE.IcosahedronGeometry(0.3, 3);
	const material = new THREE.MeshPhongMaterial({
		color: params.color_enemy,

	});

	mesh = new THREE.InstancedMesh(geometry, material, count);

	let i = 0;
	const offset = (amount - 1) / 2;

	matrix = new THREE.Matrix4();

	for (let x = 0; x < amount; x++) {

		for (let y = 0; y < amount; y++) {

			for (let z = 0; z < amount; z++) {
				matrix.setPosition(offset - x, offset - y, offset - z);
				mesh.setMatrixAt(i, matrix);
				mesh.setColorAt(i, color);
				dummy[i] = new THREE.Object3D();
				dummy[i].position.set(offset - x, offset - y, offset - z);
				i++;

			}

		}
	}
	for (let b = 0; b < mesh.count; b++) {
		let ran = random(0, 10) * .1
		// let tw = new TWEEN.Tween(dummy[b].position)
		// 	.to({
		// 		x: random(-28, 28),
		// 		y: random(-28, 28),
		// 		z: random(-28, 28),
		// 	}, 3000)
		// 	.easing(TWEEN.Easing.Linear.None)
		// 	.delay(random(1000, 10000))
		// 	.yoyo(true)
		// 	.repeat(1000)
		// 	.start()
		// 	.onComplete(() => {});
		let tw2 = new TWEEN.Tween(dummy[b].scale)
			.to({
				x: ran,
				y: ran,
				z: ran,
			}, 1000)
			.delay(random(1000, 10000))
			.easing(TWEEN.Easing.Linear.None)
			// .yoyo(true)
			// .repeat(1000)
			.start()
			.onComplete(() => {});
	}

	scene.add(mesh);

	renderer = new THREE.WebGLRenderer({
		antialias: true,
		alpha: true,
	});
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(window.innerWidth, window.innerHeight)
	container = document.getElementById('world');
	container.appendChild(renderer.domElement);
	css()
	//Shadows
	renderer.shadowMap.enabled = true;
	renderer.shadowMap.type = THREE.PCFSoftShadowMap;
	controls = new OrbitControls(camera, renderer.domElement);
	window.addEventListener('resize', onWindowResize);
	document.addEventListener('mousemove', onMouseMove);

}

function onWindowResize() {

	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize(window.innerWidth, window.innerHeight);

}

function onMouseMove(event) {

	event.preventDefault();
	mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
	mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

}

function animate() {

	requestAnimationFrame(animate);
	render();
	TWEEN.update();
	mesh.instanceMatrix.needsUpdate = true;
	for (let d = 0; d < mesh.count; d++) {
		dummy[d].updateMatrix();
		mesh.setMatrixAt(d, dummy[d].matrix);
	}
}

function render() {

	raycaster.setFromCamera(mouse, camera);

	const intersection = raycaster.intersectObject(mesh);

	if (intersection.length > 0) {

		const instanceId = intersection[0].instanceId;

		mesh.setColorAt(instanceId, color.setHex(Math.random() * 0xffffff));
		mesh.instanceColor.needsUpdate = true;

	}

	renderer.render(scene, camera);


}