WebGL - Sample Application
WebGL - Sample Application: Drawing a Triangle
Section titled “WebGL - Sample Application: Drawing a Triangle”Having covered the basics of WebGL concepts like contexts, geometry, buffers, and shaders, let’s put it all together in a minimal, complete example: drawing a simple 2D triangle. This illustrates the fundamental structure and workflow of a WebGL application.
Structure of a WebGL Application
Section titled “Structure of a WebGL Application”A typical WebGL application involves:
- HTML: A
<canvas>element to serve as the drawing surface. - JavaScript: The main logic orchestrating WebGL API calls. It prepares data, compiles shaders, sets up buffers, configures attributes/uniforms, and triggers drawing commands.
- GLSL (Shader Code): Vertex and Fragment shader programs defining how the GPU processes vertices and determines pixel colors.
JavaScript acts as the conductor, preparing everything and sending instructions and data to the GPU via the WebGL API. The GLSL shaders execute on the GPU to perform the heavy lifting of rendering.
Sample Application: Drawing a 2D Triangle
Section titled “Sample Application: Drawing a 2D Triangle”This example demonstrates the core steps to draw a static, single-color triangle.
<!doctype html><html lang="en"><head> <meta charset="utf-8"> <title>WebGL Simple Triangle</title> <style>canvas { border: 1px solid black; }</style></head><body> <canvas width="300" height="300" id="my_Canvas"></canvas>
<script> /* Step 1: Prepare the canvas and get WebGL context */ const canvas = document.getElementById('my_Canvas'); const gl = canvas.getContext('webgl'); // Request WebGL 1 context
if (!gl) { alert('WebGL is not supported by your browser.'); throw new Error('WebGL not supported'); }
/* Step 2: Define the geometry and store it in buffer objects */ // Define vertices for a triangle (X, Y coordinates) const vertices = new Float32Array([ -0.5, 0.5, // Top vertex -0.5, -0.5, // Bottom-left vertex 0.0, -0.5 // Bottom-right vertex (Modified for asymmetry) ]);
// Create a buffer object on the GPU const vertexBuffer = gl.createBuffer();
// Bind the buffer to the ARRAY_BUFFER target (making it the active VBO) gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// Pass the vertex data (TypedArray) to the currently bound buffer gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Unbind the buffer (good practice, though not strictly necessary here) // gl.bindBuffer(gl.ARRAY_BUFFER, null);
/* Step 3: Create and compile Shader programs */
// Vertex shader source code (GLSL ES 1.00) const vsSource = ` attribute vec2 aVertexPosition; // Input vertex position (2D) void main(void) { // Output position in clip space (XY from attribute, Z=0, W=1) gl_Position = vec4(aVertexPosition, 0.0, 1.0); } `;
// Fragment shader source code (GLSL ES 1.00) const fsSource = ` // Set precision for float variables (required in fragment shaders) precision mediump float; void main(void) { // Output color: Red (R=1, G=0, B=0, Alpha=1) gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `;
// Helper function to compile a shader function compileShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error(`Shader compilation error (${type === gl.VERTEX_SHADER ? 'Vertex' : 'Fragment'}):`, gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; }
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fsSource);
// Create a shader program object to link shaders const shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { console.error('Shader program linking error:', gl.getProgramInfoLog(shaderProgram)); throw new Error('Shader program link failed'); }
// Tell WebGL to use this program for subsequent drawing gl.useProgram(shaderProgram);
/* Step 4: Associate the shader programs to buffer objects */
// Get the location (index) of the attribute in the shader program const positionAttributeLocation = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
// Ensure the vertex buffer is bound again (if it was unbound) gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// Tell WebGL how to read data from the bound buffer into the attribute const numComponents = 2; // 2 values (X, Y) per vertex const type = gl.FLOAT; // Data type is 32-bit float const normalize = false; // Don't normalize the data const stride = 0; // 0 = use size * sizeof(type) to get next vertex const offset = 0; // Start at the beginning of the buffer gl.vertexAttribPointer( positionAttributeLocation, numComponents, type, normalize, stride, offset);
// Enable the vertex attribute array gl.enableVertexAttribArray(positionAttributeLocation);
/* Step 5: Drawing the required object (triangle) */
// Set the clear color (color used when clearing the canvas) gl.clearColor(0.8, 0.8, 0.8, 1.0); // Light grey
// Clear the color buffer with the specified clear color gl.clear(gl.COLOR_BUFFER_BIT);
// Set the viewport (map NDC -1 to 1 to canvas pixels 0 to width/height) gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Draw the triangle const primitiveType = gl.TRIANGLES; // Draw filled triangles const drawOffset = 0; // Start from the first vertex const vertexCount = 3; // Draw 3 vertices (making one triangle) gl.drawArrays(primitiveType, drawOffset, vertexCount);
</script>
</body></html>Expected Result:
This code will display a solid red triangle on a light grey background within the 300x300 canvas.
Summary of Steps:
Section titled “Summary of Steps:”-
Get Context: Obtain the WebGL rendering context from an HTML
<canvas>. -
Define Geometry & Buffers: Create JavaScript Typed Arrays for vertex data. Create WebGL buffer objects (
gl.createBuffer), bind them (gl.bindBuffer), and upload the data (gl.bufferData). -
Create & Compile Shaders: Write GLSL code for vertex and fragment shaders. Compile them (
gl.compileShader) and link them into a shader program (gl.createProgram,gl.attachShader,gl.linkProgram). Check for errors at each stage. -
Associate Attributes/Buffers: Get attribute locations (
gl.getAttribLocation). Bind the appropriate vertex buffer. Configure how the attribute reads data from the buffer (gl.vertexAttribPointer). Enable the attribute (gl.enableVertexAttribArray). (Uniforms would also be set here if used). -
Draw: Set the viewport (
gl.viewport), clear the canvas (gl.clearColor,gl.clear). Issue the draw command (gl.drawArraysorgl.drawElements) using the configured program and buffers.
These core steps form the foundation for all WebGL rendering.