WebGL - Scaling
WebGL - Scaling Objects
Section titled “WebGL - Scaling Objects”This chapter demonstrates how to scale (resize) objects, specifically a triangle, using WebGL transformations.
Understanding Scaling
Section titled “Understanding Scaling”Scaling involves changing the size of an object by multiplying its vertex coordinates by scaling factors. For instance, multiplying the vertices [x, y, z] by a factor of 2 results in [2x, 2y, 2z], doubling the object’s size. Scaling can be uniform (same factor for x, y, z) or non-uniform (different factors).
In WebGL, scaling is typically achieved using a transformation matrix. We create a 4x4 matrix containing the desired scaling factors along its diagonal and multiply the object’s vertex positions by this matrix within the vertex shader. This operation modifies the final position (gl_Position) of each vertex.
A typical scaling matrix looks like this:
Sx 0 0 0 0 Sy 0 0 0 0 Sz 0 0 0 0 1Where Sx, Sy, and Sz are the scaling factors along the X, Y, and Z axes, respectively.
Core Steps for Scaling
Section titled “Core Steps for Scaling”Implementing scaling in WebGL involves these key steps:
Step 1: Setup Canvas and WebGL Context
Obtain the HTML canvas element and acquire the WebGL rendering context using canvas.getContext('webgl'). Add checks to ensure the context is successfully obtained.
Step 2: Define Geometry and Buffers
Define the vertex coordinates for the object (e.g., a triangle) using JavaScript arrays. Create WebGL buffer objects (VBOs) and load the vertex data into them using gl.bufferData with Float32Array.
const vertices = new Float32Array([ -0.5, 0.5, 0.0, // Top-left -0.5, -0.5, 0.0, // Bottom-left 0.5, -0.5, 0.0 // Bottom-right]);Step 3: Create and Compile Shaders
Write, compile, and link the vertex and fragment shaders into a WebGL program. Include error checking for compilation and linking.
- Vertex Shader: Define an attribute for vertex coordinates (e.g.,
a_position) and a uniform for the scaling matrix (e.g.,u_scaleMatrix). Multiply the position by the matrix:gl_Position = u_scaleMatrix * a_position;. Remember that vertex positions are oftenvec3, but need to bevec4for matrix multiplication (usevec4(a_position, 1.0)).
// Vertex Shader (GLSL ES 1.00)attribute vec3 a_position;uniform mat4 u_scaleMatrix;
void main() { gl_Position = u_scaleMatrix * vec4(a_position, 1.0);}- Fragment Shader: Set the output color using
gl_FragColor. Define precision usingprecision mediump float;.
// Fragment Shader (GLSL ES 1.00)precision mediump float;
void main() { gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0); // Orange color}Step 4: Link Shaders and Buffers
Get the location of the attribute (a_position) and the uniform (u_scaleMatrix). Enable the vertex attribute array and configure it using gl.vertexAttribPointer to read data from the bound VBO.
Step 5: Define and Apply Scaling Matrix
Define the scaling factors (e.g., scaleX, scaleY, scaleZ). Create the 4x4 scaling matrix as a Float32Array in column-major order. Pass this matrix to the vertex shader using gl.uniformMatrix4fv.
Step 6: Draw the Object
Clear the canvas, set the viewport, and use gl.drawArrays(gl.TRIANGLES, 0, 3) to render the scaled triangle.
Example: Scaling a Triangle
Section titled “Example: Scaling a Triangle”This example demonstrates scaling a triangle to be 1.5 times taller (Y-axis scaling).
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>WebGL Scaling Example</title> <style> body { margin: 0; overflow: hidden; background-color: #555; } canvas { display: block; } </style></head><body> <canvas id="webgl-canvas" width="400" height="400"></canvas>
<script> // --- Shader Sources --- const vsSource = ` attribute vec3 a_position; uniform mat4 u_scaleMatrix; void main() { gl_Position = u_scaleMatrix * vec4(a_position, 1.0); } `;
const fsSource = ` precision mediump float; void main() { gl_FragColor = vec4(0.0, 0.7, 0.2, 1.0); // Green color } `;
// --- Main WebGL Initialization --- const canvas = document.getElementById('webgl-canvas'); const gl = canvas.getContext('webgl');
if (!gl) { alert('WebGL is not supported by your browser.'); throw new Error('WebGL not supported'); }
// --- Shader Compilation & Linking --- function createShader(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:', gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; }
function createProgram(gl, vsSource, fsSource) { const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource); if (!vertexShader || !fragmentShader) return null;
const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error('Program linking error:', gl.getProgramInfoLog(program)); gl.deleteProgram(program); return null; } return program; }
const shaderProgram = createProgram(gl, vsSource, fsSource); if (!shaderProgram) throw new Error('Failed to create shader program.');
// --- Geometry and Buffers --- const vertices = new Float32Array([ -0.5, 0.5, 0.0, // Top-left -0.5, -0.5, 0.0, // Bottom-left 0.5, -0.5, 0.0 // Bottom-right ]);
const vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// --- Attribute Location and Configuration --- const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'a_position'); if (positionAttribLocation < 0) { console.error("Attribute 'a_position' not found"); } gl.enableVertexAttribArray(positionAttribLocation); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // Re-bind necessary before vertexAttribPointer gl.vertexAttribPointer( positionAttribLocation, // Attribute location 3, // Number of components per attribute (x, y, z) gl.FLOAT, // Type of data false, // Normalized? 0, // Stride (0 = auto) 0 // Offset (0 = start) );
// --- Uniform Location and Scaling Matrix --- const scaleMatrixLocation = gl.getUniformLocation(shaderProgram, 'u_scaleMatrix'); if (!scaleMatrixLocation) { console.error("Uniform 'u_scaleMatrix' not found"); }
const scaleX = 1.0; const scaleY = 1.5; // Scale Y by 1.5 const scaleZ = 1.0;
// Scaling Matrix (Column Major Order for WebGL) const scaleMatrix = new Float32Array([ scaleX, 0.0, 0.0, 0.0, 0.0, scaleY, 0.0, 0.0, 0.0, 0.0, scaleZ, 0.0, 0.0, 0.0, 0.0, 1.0 ]);
gl.useProgram(shaderProgram); // Activate the program *before* setting uniforms gl.uniformMatrix4fv(scaleMatrixLocation, false, scaleMatrix);
// --- Drawing --- gl.clearColor(0.1, 0.1, 0.1, 1.0); // Dark gray background gl.enable(gl.DEPTH_TEST); // Enable depth testing for 3D gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.drawArrays( gl.TRIANGLES, // Primitive type 0, // Starting index 3 // Number of vertices to draw );
</script></body></html>Expected Output:
The code will render a green triangle centered horizontally. Compared to an unscaled triangle (which would fit roughly within a square from y=-0.5 to y=0.5), this triangle will appear stretched vertically, extending from y = -0.75 to y = 0.75 due to the scaleY = 1.5 factor.
Further Exploration:
Section titled “Further Exploration:”- Try changing the
scaleX,scaleY, andscaleZvalues to see different scaling effects. - Combine scaling with translation and rotation by multiplying their respective matrices. Remember that matrix multiplication order matters! (Typically Scale -> Rotate -> Translate).
- For complex transformations, consider using a math library like
gl-matrix(https://github.com/toji/gl-matrix) to simplify matrix operations.