WebGL Buffers Overview
Buffers are areas in GPU memory used to store vertex data, index data, and other information required for graphics rendering. Using buffers allows efficient data transfer from CPU to GPU.
Buffer Types
1. Vertex Buffer Object (VBO)
Stores vertex attribute data such as positions, colors, normals, texture coordinates, etc.
2. Index Buffer Object (IBO/EBO)
Stores vertex indices to define how primitives are connected, reducing duplicate vertex data.
3. Vertex Array Object (VAO)
Stores the state of vertex attribute configurations, simplifying setup before draw calls.
VBO (Vertex Buffer Object) Details
Creating and Using VBO
javascript// 1. Create buffer const vbo = gl.createBuffer(); // 2. Bind buffer (specify the type of buffer to operate on) gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 3. Upload data to GPU const vertices = new Float32Array([ // Position(x, y, z) Color(r, g, b) -0.5, -0.5, 0.0, 1.0, 0.0, 0.0, // Vertex 1 0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // Vertex 2 0.0, 0.5, 0.0, 0.0, 0.0, 1.0 // Vertex 3 ]); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); // 4. Configure vertex attribute pointer gl.vertexAttribPointer( 0, // Attribute location (corresponds to layout(location=0) in shader) 3, // Number of components per attribute (3: x, y, z) gl.FLOAT, // Data type false, // Normalized 6 * 4, // Stride (bytes per vertex: 6 floats * 4 bytes) 0 // Offset ); gl.enableVertexAttribArray(0); // Enable position attribute // Configure color attribute gl.vertexAttribPointer( 1, // Attribute location 3, // Number of components (r, g, b) gl.FLOAT, false, 6 * 4, // Stride 3 * 4 // Offset (skip position data) ); gl.enableVertexAttribArray(1); // Enable color attribute
Buffer Usage Modes
javascript// gl.STATIC_DRAW: Data doesn't change often, drawn many times gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); // gl.DYNAMIC_DRAW: Data changes often, drawn many times gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW); // gl.STREAM_DRAW: Data changes every draw call gl.bufferData(gl.ARRAY_BUFFER, data, gl.STREAM_DRAW);
IBO/EBO (Index Buffer Object) Details
Creating and Using IBO
javascript// Vertex data (4 vertices define a quad) const vertices = new Float32Array([ -0.5, 0.5, 0.0, // Top-left (0) -0.5, -0.5, 0.0, // Bottom-left (1) 0.5, -0.5, 0.0, // Bottom-right (2) 0.5, 0.5, 0.0 // Top-right (3) ]); // Index data (2 triangles = 6 indices) const indices = new Uint16Array([ 0, 1, 2, // First triangle 0, 2, 3 // Second triangle ]); // Create and bind VBO gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); // Create and bind IBO const ibo = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); // Draw (using indexed drawing) gl.drawElements( gl.TRIANGLES, // Draw mode 6, // Number of indices gl.UNSIGNED_SHORT, // Index data type 0 // Offset );
VAO (Vertex Array Object) Details
VAO stores all vertex attribute configuration states, including:
- Enabled vertex attributes
- Configuration for each attribute (size, type, stride, offset)
- Bound VBO
WebGL 2.0 / WebGL 1.0 + OES_vertex_array_object Extension
javascript// Create VAO const vao = gl.createVertexArray(); // WebGL 2.0 // const vao = ext.createVertexArrayOES(); // Using extension // Bind VAO (subsequent configurations will be stored in VAO) gl.bindVertexArray(vao); // Configure vertex attributes (these configurations will be recorded by VAO) gl.bindBuffer(gl.ARRAY_BUFFER, positionVBO); gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(0); gl.bindBuffer(gl.ARRAY_BUFFER, colorVBO); gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(1); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); // Unbind VAO gl.bindVertexArray(null); // When drawing, just bind VAO gl.bindVertexArray(vao); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
Complete Example Code
javascriptclass Mesh { constructor(gl) { this.gl = gl; this.vertexCount = 0; this.indexCount = 0; // WebGL 2.0 create VAO this.vao = gl.createVertexArray(); this.vbo = gl.createBuffer(); this.ibo = gl.createBuffer(); } setVertices(vertices, attributes) { const gl = this.gl; gl.bindVertexArray(this.vao); // Upload vertex data gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); // Configure attributes let offset = 0; const stride = attributes.reduce((sum, attr) => sum + attr.size, 0) * 4; attributes.forEach((attr, index) => { gl.vertexAttribPointer( index, attr.size, gl.FLOAT, false, stride, offset ); gl.enableVertexAttribArray(index); offset += attr.size * 4; }); this.vertexCount = vertices.length / (stride / 4); } setIndices(indices) { const gl = this.gl; gl.bindVertexArray(this.vao); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); this.indexCount = indices.length; } draw() { const gl = this.gl; gl.bindVertexArray(this.vao); if (this.indexCount > 0) { gl.drawElements(gl.TRIANGLES, this.indexCount, gl.UNSIGNED_SHORT, 0); } else { gl.drawArrays(gl.TRIANGLES, 0, this.vertexCount); } } } // Usage example const mesh = new Mesh(gl); mesh.setVertices( new Float32Array([/* vertex data */]), [ { size: 3 }, // Position { size: 3 }, // Color { size: 2 } // Texture coordinates ] ); mesh.setIndices(new Uint16Array([/* index data */])); mesh.draw();
Performance Optimization Tips
- Reduce state switching: Use VAO to reduce configuration overhead before drawing
- Merge buffers: Combine multiple small meshes into one large buffer
- Use indexed drawing: Reduce vertex data duplication
- Choose appropriate draw modes:
STATIC_DRAW: Static geometryDYNAMIC_DRAW: Frequently updated dataSTREAM_DRAW: Data updated every frame
- Batch drawing: Use instanced rendering to draw multiple identical objects