Seeking optimization tips: big map with trees and rocks

Hello! I’m just starting my journey with three.js (actually react fiber).

I’m creating a test board that will be the basis of a map. I want to divide the board into individual fields. At the moment, a green box is displayed on a given field or not. Ultimately, some object, e.g. a tree, a stone, etc., will be located in each field of the board.

Unfortunately, now with 300,000 triangles, the number of fps drops drastically. What are the best practices for optimizing this type of task? It doesn’t matter whether I display the scene in close-up (only a few squares are visible) or the whole board, I have equally low fps.

I read that Frustum Culling in three.js does not work on instances, and when I tried manually to calculate whether a given rectangle is visible, the fps dropped even more.

Could I ask for tips and advice?

Here’s what my component code looks like:

import React, { useMemo } from 'react';

import { Instance, Instances } from '@react-three/drei';

export interface GridItem {
    visible: boolean;
    comment: string;
    value: number;
}

interface GridProps {
    grid: GridItem[][];
    size?: number;
}

const Grid: React.FC<GridProps> = ({ grid, size = 200 }) => {
    const gridHalfSize = size / 2;
    const countX = grid.length;
    const countY = grid[0].length;

    // filter squares (visible == true)
    const visibleSquares = useMemo(() => {
        const squares = [];
        for (let x = 0; x < countX; x++) {
            for (let y = 0; y < countY; y++) {
                if (grid[x][y].visible) {
                    squares.push([x - gridHalfSize, y - gridHalfSize]);
                }
            }
        }
        return squares;
    }, [grid, gridHalfSize, countX, countY]);

    return (
        <Instances limit={visibleSquares.length} frustumCulled={false}>
            <boxGeometry args={[1, 1, 0.01]} />
            <meshBasicMaterial color="green" />
            {visibleSquares.map(([x, y], index) => (
                <Instance
                    key={index}
                    position={[x + 0.5, 0.01, y + 0.5]}
                    rotation={[Math.PI / 2, 0, 0]}
                />
            ))}
        </Instances>
    );
};

export default Grid;

Try LOD,

Here is a vanilla example of LOD

for R3F, you can use DREI Detailed

I have created an enhanced version of InstancedMesh that also allows frustum culling and fast raycasting.

const trees = new InstancedMesh2(main.renderer, count, {
  cullingType: CullingBVH,
  geometry: treeGLTF.geometry,
  material: treeGLTF.material,
  onInstanceCreation: (obj, index) => {
    obj.position.setX(Math.random() * terrainSize - terrainSize / 2).setZ(Math.random() * terrainSize - terrainSize / 2);
    obj.scale.setScalar(Math.random() * 0.1 + 0.1);
    obj.rotateY(Math.random() * Math.PI * 2).rotateZ(Math.random() * 0.3 - 0.15);
  }
});

Example with 1 milion trees:

I should publish the package this evening, now a temporary one is in use.

1 Like

Thank you for your quick response. The optimization using LOD (Level of Detail) will certainly be very useful in the later stage when I will be displaying proper elements such as trees instead of squares.

However, right now, in a simplified scene, I’m displaying primitive boxGeometry, which cannot be simplified further. Therefore, I would still be looking for another way of optimization.

Interestingly, despite these being primitive shapes, the frame rate remains equally low whether I’m viewing the entire board or zooming in with the camera.

@seanwasere Could I ask if the use of LOD will also work for Instances? Are there perhaps other techniques that could help optimize performance in my case?

Thank you again for your help.

Wow, that’s fantastic to see so useful library!

I agree that frustum culling should significantly improve performance. This is particularly relevant in my case, as I’m experiencing low frame rates consistently, regardless of the camera’s zoom level. This means that even when the camera is zoomed in and fewer elements are visible on the screen, the frame rate does not improve.

Could you possibly provide an example or guide me on how to modify my code to use your enhanced version of InstancedMesh in react fiber? I’m particularly interested in the simplest way to integrate it into my current setup.

1 Like

Unfortunately I do not know react, however the creator of r3f is very active on the forum, maybe he could help you (I would like to have an example using r3f in the README of my library).

I hope I’ve addressed my call to @drcmda correctly. @drcmda perhaps you could find the time to prepare an example of using your library for the community? Thank you in advance.