WebGL - 缩放
WebGL - 缩放对象
Section titled “WebGL - 缩放对象”本章演示了如何使用 WebGL 变换来缩放(调整大小)对象,特别是三角形。
缩放涉及通过将对象的顶点坐标乘以缩放因子来改变对象的大小。例如,将顶点 [x, y, z] 乘以因子 2 会得到 [2x, 2y, 2z],从而使对象的大小加倍。缩放可以是均匀的(x、y、z 使用相同的因子)或非均匀的(使用不同的因子)。
在 WebGL 中,缩放通常使用变换矩阵实现。我们创建一个 4x4 矩阵,其对角线上包含所需的缩放因子,并在顶点着色器中将对象的顶点位置乘以该矩阵。此操作会修改每个顶点的最终位置(gl_Position)。
典型的缩放矩阵如下所示:
Sx 0 0 0 0 Sy 0 0 0 0 Sz 0 0 0 0 1其中 Sx、Sy 和 Sz 分别是沿 X、Y 和 Z 轴的缩放因子。
缩放的核心步骤
Section titled “缩放的核心步骤”在 WebGL 中实现缩放涉及以下关键步骤:
步骤 1:设置 Canvas 和 WebGL 上下文
获取 HTML canvas 元素,并使用 canvas.getContext('webgl') 获取 WebGL 渲染上下文。添加检查以确保成功获取上下文。
步骤 2:定义几何体和缓冲区
使用 JavaScript 数组定义对象的顶点坐标(例如,三角形)。创建 WebGL 缓冲区对象(VBO),并使用 gl.bufferData 和 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]);步骤 3:创建和编译着色器
编写、编译并将顶点着色器和片段着色器链接到 WebGL 程序中。包含编译和链接的错误检查。
- 顶点着色器(Vertex Shader):为顶点坐标定义一个属性(例如,
a_position),并为缩放矩阵定义一个统一变量(uniform)(例如,u_scaleMatrix)。将位置乘以矩阵:gl_Position = u_scaleMatrix * a_position;。记住顶点位置通常是vec3,但对于矩阵乘法需要是vec4(使用vec4(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):使用
gl_FragColor设置输出颜色。使用precision 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}步骤 4:链接着色器和缓冲区
获取属性(a_position)和统一变量(u_scaleMatrix)的位置。启用顶点属性数组,并使用 gl.vertexAttribPointer 配置它以从绑定的 VBO 读取数据。
步骤 5:定义和应用缩放矩阵
定义缩放因子(例如,scaleX、scaleY、scaleZ)。以列主序(column-major order)将 4x4 缩放矩阵创建为 Float32Array。使用 gl.uniformMatrix4fv 将此矩阵传递给顶点着色器。
步骤 6:绘制对象
清除 canvas,设置视口(viewport),并使用 gl.drawArrays(gl.TRIANGLES, 0, 3) 渲染缩放后的三角形。
示例:缩放一个三角形
Section titled “示例:缩放一个三角形”此示例演示将一个三角形沿 Y 轴缩放至原高的 1.5 倍。
<!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.uniformMatrix44fv(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>预期输出:
此代码将渲染一个水平居中的绿色三角形。与未缩放的三角形(大致位于 y=-0.5 到 y=0.5 的正方形内)相比,由于 scaleY = 1.5 因子,此三角形将沿垂直方向拉伸,从 y = -0.75 延伸到 y = 0.75。
进一步探索:
Section titled “进一步探索:”- 尝试改变
scaleX、scaleY和scaleZ的值,查看不同的缩放效果。 - 通过乘以各自的矩阵,将缩放与平移和旋转结合起来。记住矩阵乘法顺序很重要!(通常是 缩放 -> 旋转 -> 平移)。
- 对于复杂的变换,考虑使用数学库,例如
gl-matrix(https://github.com/toji/gl-matrix),以简化矩阵操作。