Generating more than 7-8 cubes causes low fps

Hey I’m a Unity developer and I’ve been trying to learn three.js for the last 2 weeks and I can say that I really like it so far. And while I was trying something on my own, I noticed that creating 7-8 or more cubes on the screen caused a lot of FPS drops, but I couldn’t figure out the reason. Can anyone help?

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
scene.background = new THREE.Color(0x3498db);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; 


const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.castShadow = true;
directionalLight.position.set(0, 1, 0);
directionalLight.shadow.mapSize.width = 512; 
directionalLight.shadow.mapSize.height = 512;
scene.add(directionalLight);


const size = 10;
const divisions = 10;
const gridHelper = new THREE.GridHelper(size, divisions);
gridHelper.position.y = 0.1;
scene.add(gridHelper);

camera.position.set(0, 7, 7);
camera.rotation.set(
    THREE.MathUtils.degToRad(-45),
    THREE.MathUtils.degToRad(0),
    THREE.MathUtils.degToRad(0)
);


const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);

const geometry = new THREE.PlaneGeometry(10, 10);
const material = new THREE.MeshToonMaterial({ color: 0x27ae60, side: THREE.DoubleSide });
const plane = new THREE.Mesh(geometry, material);
plane.receiveShadow = true;
plane.rotation.set(Math.PI / 2, 0, 0);
scene.add(plane);

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

const dirtMaterial = new THREE.MeshToonMaterial({ color: 0xd35400 });
const geoDirt = new THREE.BoxGeometry(1, 0.2, 1);
window.addEventListener('click', (event) => {
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);

    const intersects = raycaster.intersectObject(plane);
    if (intersects.length > 0) {
        const intersectPoint = intersects[0].point;
        const gridSize = 1;
        const x = Math.floor(intersectPoint.x / gridSize) * gridSize + gridSize / 2;
        const z = Math.floor(intersectPoint.z / gridSize) * gridSize + gridSize / 2;

        const dirt = new THREE.Mesh(geoDirt, dirtMaterial);
        dirt.position.set(x, 0.1, z);
        dirt.receiveShadow = true;
        dirt.castShadow = true;
        scene.add(dirt);

    }
});

let isMouseDown = false; 
let startX = 0; 
let angle = 0; 
const radius = 10;


const deltaX = 0 - startX;
const rotationSpeed = 0.01; 
angle -= deltaX * rotationSpeed; 

camera.position.x = radius * Math.sin(angle);
camera.position.z = radius * Math.cos(angle);
camera.lookAt(0, 0, 0);

window.addEventListener('mousedown', (event) => {
    if (event.button === 1) { 
        isMouseDown = true;
        startX = event.clientX; 
    }
});

window.addEventListener('mousemove', (event) => {
    if (isMouseDown) {
        const deltaX = event.clientX - startX; 
        const rotationSpeed = 0.01; 
        angle -= deltaX * rotationSpeed;

        camera.position.x = radius * Math.sin(angle);
        camera.position.z = radius * Math.cos(angle);
        camera.lookAt(0, 0, 0); 

        startX = event.clientX; 
    }
});

window.addEventListener('mouseup', (event) => {
    if (event.button === 1) { 
        isMouseDown = false;
    }
});


Try removing requestAnimationFrame from the animation loop. setAnimationLoop is sufficient.

6 Likes

Oh problem solved. Thank you so much! I guess I need to go read the documentation on what this actually does.

1 Like

This is a good idea, but the documentation is not a tutorial. And in your problem, three.js renderer and JS requestAnimationFrame played a role in the interaction.

You can learn a lot from good examples.

There are also many excellent ones at Collection of examples from discourse.threejs.org @PavelBoytchev. :clap:t2:

1 Like

It is confusing. Originally the renderloop worked similarly to how you had it…

let animationFunction(){
    requestAnimationFrame(animationFunction);

    //render the stuff....
}
animationFunction() // start the render loop...

But this scheme doesn’t work for things like VR where threejs needs to render the scene twice… once for each eye… which is why “renderer.setAnimationLoop()” was needed… so that THREEJS can decide when/how often to render your scene.

so now its just:

let animationFunction(){
   //render the scene
}

renderer.setAnimationLoop( animationFunction ); //Start the render loop
2 Likes