I’ve just started learning WebGL.
I am rendering multiple spheres but I’m not sure about the “bindBuffer” and “bufferData” calls inside the render loops.
I can render a single sphere with 2 million vertices no problem. But once I try to render 3 spheres with 100k vertices each (300k total, 85% less vertices), the performance starts to go down.
I want to know exactly what needs to remain inside the render loop and what doesn’t. And if there is something else I am missing.
Here is my Sphere “class”:
JavaScript
x
50
50
1
function Sphere (resolution, gl, vertex, fragment) {
2
3
const {positions, indexes} = createPositionsAndIndexes(resolution);
4
5
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertex);
6
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragment);
7
const program = createProgram(gl, vertexShader, fragmentShader);
8
9
this.x = 0;
10
this.y = 0;
11
this.z = -6;
12
this.angle = {x:0,y:0,z:0};
13
14
const positionBuffer = gl.createBuffer();
15
const indexBuffer = gl.createBuffer();
16
17
const positionLocation = gl.getAttribLocation(program, "position");
18
const viewLocation = gl.getUniformLocation(program, "view");
19
const projectionLocation = gl.getUniformLocation(program, "projection");
20
21
this.render = () => {
22
23
gl.useProgram(program);
24
25
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
26
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
27
28
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
29
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indexes), gl.STATIC_DRAW);
30
31
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
32
gl.enableVertexAttribArray(positionLocation);
33
34
const viewMatrix = glMatrix.mat4.create();
35
glMatrix.mat4.translate(viewMatrix, viewMatrix, [this.x, this.y, this.z]);
36
glMatrix.mat4.rotateX(viewMatrix, viewMatrix, this.angle.x);
37
glMatrix.mat4.rotateY(viewMatrix, viewMatrix, this.angle.y);
38
glMatrix.mat4.rotateZ(viewMatrix, viewMatrix, this.angle.z);
39
gl.uniformMatrix4fv(viewLocation, false, viewMatrix);
40
41
const projectionMatrix = glMatrix.mat4.create();
42
glMatrix.mat4.perspective(projectionMatrix, 45 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 100.0);
43
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
44
45
gl.drawElements(gl.TRIANGLES, indexes.length, gl.UNSIGNED_INT, 0);
46
47
};
48
49
}
50
And here is the main “class”:
JavaScript
1
46
46
1
document.addEventListener("DOMContentLoaded", () => {
2
3
const canvas = document.querySelector("canvas");
4
5
const width = canvas.width = canvas.clientWidth;
6
const height = canvas.height = canvas.clientHeight;
7
8
const gl = canvas.getContext("webgl2");
9
10
const sphere1 = new Sphere(300, gl, vertexShaderSource, fragmentShaderSource);
11
sphere1.x = -0.5;
12
13
const sphere2 = new Sphere(300, gl, vertexShaderSource, fragmentShaderSource);
14
sphere2.x = 0.0;
15
16
const sphere3 = new Sphere(300, gl, vertexShaderSource, fragmentShaderSource);
17
sphere3.x = +0.5;
18
19
const render = () => {
20
21
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
22
23
gl.clearColor(0, 0, 0, 0);
24
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
25
26
gl.enable(gl.DEPTH_TEST);
27
gl.clearDepth(1.0);
28
gl.depthFunc(gl.LEQUAL);
29
30
sphere1.angle.y -= 0.01;
31
sphere1.render();
32
33
sphere2.angle.y -= 0.01;
34
sphere2.render();
35
36
sphere3.angle.y -= 0.005;
37
sphere3.render();
38
39
window.requestAnimationFrame(render);
40
41
};
42
43
render();
44
45
});
46
Advertisement
Answer
You shouldn’t call bufferData at render time unless you’re changing the data in the buffer.
JavaScript
1
57
57
1
unction Sphere (resolution, gl, vertex, fragment) {
2
3
const {positions, indexes} = createPositionsAndIndexes(resolution);
4
5
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertex);
6
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragment);
7
const program = createProgram(gl, vertexShader, fragmentShader);
8
9
this.x = 0;
10
this.y = 0;
11
this.z = -6;
12
this.angle = {x:0,y:0,z:0};
13
14
const positionBuffer = gl.createBuffer();
15
const indexBuffer = gl.createBuffer();
16
17
const positionLocation = gl.getAttribLocation(program, "position");
18
const viewLocation = gl.getUniformLocation(program, "view");
19
const projectionLocation = gl.getUniformLocation(program, "projection");
20
21
// create buffers and put data in them
22
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
23
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
24
25
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
26
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indexes), gl.STATIC_DRAW);
27
28
29
this.render = () => {
30
31
gl.useProgram(program);
32
33
// bind the position buffer to the attribute
34
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
35
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
36
gl.enableVertexAttribArray(positionLocation);
37
38
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
39
40
41
const viewMatrix = glMatrix.mat4.create();
42
glMatrix.mat4.translate(viewMatrix, viewMatrix, [this.x, this.y, this.z]);
43
glMatrix.mat4.rotateX(viewMatrix, viewMatrix, this.angle.x);
44
glMatrix.mat4.rotateY(viewMatrix, viewMatrix, this.angle.y);
45
glMatrix.mat4.rotateZ(viewMatrix, viewMatrix, this.angle.z);
46
gl.uniformMatrix4fv(viewLocation, false, viewMatrix);
47
48
const projectionMatrix = glMatrix.mat4.create();
49
glMatrix.mat4.perspective(projectionMatrix, 45 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 100.0);
50
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
51
52
gl.drawElements(gl.TRIANGLES, indexes.length, gl.UNSIGNED_INT, 0);
53
54
};
55
56
}
57
you might find these articles and in particular this one