Back

Technologies:

javascript
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;
``````
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
]
]
}
``````

Tolerim
3 hours ago
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;