Back

Technologies:

javascriptjavascript
avatar
Tolerim
3 hours ago

What is the method for creating a large number of rectangles with a single color fill in three js?

For my threeJS project, I need to efficiently draw millions of rectangles with a solid fill using the provided coordinates. The program must be memory efficient and avoid consuming GPU when drawing a single shape. Drawing all shapes should ideally require only a single draw call. Despite several attempts, I have been unsuccessful in achieving this task. I would appreciate any help with the following program:

const geometry = new BufferGeometry();
// Define vertex positions as a Float32Array.
const positions = new Float32Array(4 * 3 * this.customShapes.length);
const indexArray = [];
let indexCounter = 0;

for (let i = 0; i < this.customShapes.length; i++) {
    let rectShape = this.customShapes[i];
    positions[i * 12 + 0] = rectShape.x1;
    positions[i * 12 + 1] = rectShape.y1;
    positions[i * 12 + 2] = 0;
    positions[i * 12 + 3] = rectShape.x1;
    positions[i * 12 + 4] = rectShape.y2;
    positions[i * 12 + 5] = 0;
    positions[i * 12 + 6] = rectShape.x2;
    positions[i * 12 + 7] = rectShape.y2;
    positions[i * 12 + 8] = 0;
    positions[i * 12 + 9] = rectShape.x2;
    positions[i * 12 + 10] = rectShape.y1;
    positions[i * 12 + 11] = 0;
    indexArray[i * 6 + 0] = indexCounter;
    indexArray[i * 6 + 1] = indexCounter + 1;
    indexArray[i * 6 + 2] = indexCounter + 2;
    indexArray[i * 6 + 3] = indexCounter;
    indexArray[i * 6 + 4] = indexCounter + 2;
    indexArray[i * 6 + 5] = indexCounter + 3;
    indexCounter += 4;
}

// Define geometry attributes and indices.
geometry.setAttribute('position', new BufferAttribute(positions, 3));
geometry.setIndex(indexArray);

// Create a mesh with a given color and add it to the scene.
const material = new MeshBasicMaterial({ color: color });
const mesh = new Mesh(geometry, material);
mesh.name = this.key;
group.add(mesh);
The above program does not draw one of the given rectangles with the following coordinates:

[
{
    "Coordinates": [
        [
            -0.45,
            0.89
        ],
        [
            -0.45,
            1.0
        ],
        [
            0.48,
            1.0
        ],
        [
            0.48,
            0.89
        ]
    ]
},
{
    "Coordinates": [
        [
            0.6699999999999999,
            2.69
        ],
        [
            0.6699999999999999,
            0.89
        ],
        [
            0.81,
            0.89
        ],
        [
            0.81,
            2.69
        ]
    ]
}

Answers(1)
avatar
Tolerim
3 hours ago
Verified Answer
One potential reason why this program may not work for some of the rectangles is because the coordinates are not normalized to the range of [-1, 1]. ThreeJS uses a normalized coordinate system where the x, y, and z values range from -1 to 1. To fix this issue, you can try normalizing the coordinates before passing them into the positions array. Here is an example of how to normalize the coordinates:
// Normalize coordinates to [-1, 1] range
function normalizeCoordinates(coords) {
  return {
    x: (coords[0] * 2) - 1,
    y: ((coords[1] * 2) - 1) * -1, // Flip y-axis to match ThreeJS's coordinate system
  };
}

const geometry = new BufferGeometry();
// Set up the vertex positions as a Float32Array
const positions = new Float32Array(4 * 3 * this.customShapes.length);
const indexArray = [];
let indexCounter = 0;
for (let i = 0; i < this.customShapes.length; i++) {
  let rectShape = this.customShapes[i];
  let coords1 = normalizeCoordinates(rectShape.Coordinates[0]);
  let coords2 = normalizeCoordinates(rectShape.Coordinates[1]);
  let coords3 = normalizeCoordinates(rectShape.Coordinates[2]);
  let coords4 = normalizeCoordinates(rectShape.Coordinates[3]);

  positions[i * 12 + 0] = coords1.x;
  positions[i * 12 + 1] = coords1.y;
  positions[i * 12 + 2] = 0;
  positions[i * 12 + 3] = coords2.x;
  positions[i * 12 + 4] = coords2.y;
  positions[i * 12 + 5] = 0;
  positions[i * 12 + 6] = coords3.x;
  positions[i * 12 + 7] = coords3.y;
  positions[i * 12 + 8] = 0;
  positions[i * 12 + 9] = coords4.x;
  positions[i * 12 + 10] = coords4.y;
  positions[i * 12 + 11] = 0;

  indexArray[i * 6 + 0] = indexCounter;
  indexArray[i * 6 + 1] = indexCounter + 1;
  indexArray[i * 6 + 2] = indexCounter + 2;
  indexArray[i * 6 + 3] = indexCounter;
  indexArray[i * 6 + 4] = indexCounter + 2;
  indexArray[i * 6 + 5] = indexCounter + 3;

  indexCounter += 4;
}

geometry.setAttribute('position', new BufferAttribute(positions, 3));
geometry.setIndex(indexArray);

const material = new MeshBasicMaterial({ color: color });

// Create a mesh and add it to the scene
const mesh = new Mesh(geometry, material);
mesh.name = this.key;
group.add(mesh);
In this example, we created a normalizeCoordinates function that takes in an array of x,y coordinates and returns an object with normalized x,y values. The normalizeCoordinates function multiplies each coordinate by 2 and subtracts 1 to shift the range from [0,1] to [-1,1]. We also flipped the y-axis by multiplying the y-coordinate by -1, to match ThreeJS's coordinate system. We then called normalizeCoordinates on each corner point and used those normalized coordinates to set the vertex positions in the positions array.
;