如何实现3D谢尔宾斯基三角旋转?附WebGL代码求助
Got it, let's get your 3D Sierpinski Gasket spinning smoothly! Your current code renders a static version because there's no transformation pipeline set up—right now, vertices are drawn directly without any rotation or perspective projection. Here's how to add the animation:
Step 1: Update the Vertex Shader
We need to add uniform variables to handle model-view and projection matrices, which will apply rotation and perspective to our 3D geometry. Replace your existing vertex shader with this:
attribute vec3 vPosition; attribute vec3 vColor; varying vec4 color; // Uniforms for transformation matrices uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; void main() { // Apply transformations to the vertex position gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0); color = vec4(vColor, 1.0); }
Step 2: Add Global Variables for Animation
Add these global variables at the top of your 3DSierpinskiTriangle.js to track rotation state and matrix locations:
"use strict"; var canvas; var gl; var points = []; var colors = []; var NumTimesToSubdivide = 3; // New variables for animation and transformations var angle = 0.0; var modelViewMatrixLoc, projectionMatrixLoc;
Step 3: Initialize Matrix Uniforms in init()
In your init() function, after setting up the shader program, add code to get uniform locations and set up a perspective projection:
// Load shaders and initialize attribute buffers var program = initShaders(gl, "vertex-shader", "fragment-shader"); gl.useProgram(program); // Get locations of our uniform matrices from the shader modelViewMatrixLoc = gl.getUniformLocation(program, "modelViewMatrix"); projectionMatrixLoc = gl.getUniformLocation(program, "projectionMatrix"); // Set up a perspective projection to give 3D depth var projectionMatrix = perspective(60, canvas.width/canvas.height, 0.1, 100); gl.uniformMatrix4fv(projectionMatrixLoc, false, flatten(projectionMatrix)); // ... rest of your init() code remains the same ...
Step 4: Modify render() to Animate Rotation
Update the render() function to increment the rotation angle each frame, compute the model-view matrix (which handles translation and rotation), and request the next animation frame:
function render() { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Increment rotation angle (adjust 0.5 to change rotation speed) angle += 0.5; // Create model-view matrix: first translate the gasket back (so it's visible), then rotate var modelViewMatrix = translate(0, 0, -2.5); // Move back 2.5 units along Z-axis modelViewMatrix = mult(modelViewMatrix, rotateX(angle)); // Rotate around X-axis modelViewMatrix = mult(modelViewMatrix, rotateY(angle * 0.7)); // Rotate around Y-axis (slower speed for dynamic look) // Pass the updated model-view matrix to the shader gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(modelViewMatrix)); gl.drawArrays(gl.TRIANGLES, 0, points.length); // Request next frame to keep the animation running requestAnimationFrame(render); }
Key Notes
- Perspective Projection: The
perspective()function (from yourMV.jslibrary) creates a frustum that gives our 3D object depth, making closer triangles appear larger. - Model-View Matrix: This matrix combines translation (moving the object back so it's within the camera's view) and rotation around X/Y axes. Adjust the rotation speed (the
0.5and0.7values) to make the animation faster or slower. - requestAnimationFrame: This browser API ensures smooth animation by syncing with the screen's refresh rate, which is better than using
setInterval.
Full Updated JavaScript Code
Here's the complete modified 3DSierpinskiTriangle.js for reference:
"use strict"; var canvas; var gl; var points = []; var colors = []; var NumTimesToSubdivide = 3; var angle = 0.0; var modelViewMatrixLoc, projectionMatrixLoc; window.onload = function init() { canvas = document.getElementById("gl-canvas"); gl = WebGLUtils.setupWebGL(canvas); if (!gl) { alert("WebGL isn't available"); } // Initialize vertices for the tetrahedron var vertices = [ vec3(0.0000, 0.0000, -1.0000), vec3(0.0000, 0.9428, 0.3333), vec3(-0.8165, -0.4714, 0.3333), vec3(0.8165, -0.4714, 0.3333) ]; divideTetra(vertices[0], vertices[1], vertices[2], vertices[3], NumTimesToSubdivide); // Configure WebGL gl.viewport(0, 0, canvas.width, canvas.height); gl.clearColor(1.0, 1.0, 1.0, 1.0); gl.enable(gl.DEPTH_TEST); // Load shaders and initialize attribute buffers var program = initShaders(gl, "vertex-shader", "fragment-shader"); gl.useProgram(program); // Get uniform matrix locations modelViewMatrixLoc = gl.getUniformLocation(program, "modelViewMatrix"); projectionMatrixLoc = gl.getUniformLocation(program, "projectionMatrix"); // Set up perspective projection var projectionMatrix = perspective(60, canvas.width/canvas.height, 0.1, 100); gl.uniformMatrix4fv(projectionMatrixLoc, false, flatten(projectionMatrix)); // Color buffer setup var cBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, cBuffer); gl.bufferData(gl.ARRAY_BUFFER, flatten(colors), gl.STATIC_DRAW); var vColor = gl.getAttribLocation(program, "vColor"); gl.vertexAttribPointer(vColor, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(vColor); // Position buffer setup var vBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer); gl.bufferData(gl.ARRAY_BUFFER, flatten(points), gl.STATIC_DRAW); var vPosition = gl.getAttribLocation(program, "vPosition"); gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(vPosition); render(); }; function triangle(a, b, c, color) { var baseColors = [ vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, 0.0) ]; colors.push(baseColors[color]); points.push(a); colors.push(baseColors[color]); points.push(b); colors.push(baseColors[color]); points.push(c); } function tetra(a, b, c, d) { triangle(a, c, b, 0); triangle(a, c, d, 1); triangle(a, b, d, 2); triangle(b, c, d, 3); } function divideTetra(a, b, c, d, count) { if (count === 0) { tetra(a, b, c, d); } else { var ab = mix(a, b, 0.5); var ac = mix(a, c, 0.5); var ad = mix(a, d, 0.5); var bc = mix(b, c, 0.5); var bd = mix(b, d, 0.5); var cd = mix(c, d, 0.5); --count; divideTetra(a, ab, ac, ad, count); divideTetra(ab, b, bc, bd, count); divideTetra(ac, bc, c, cd, count); divideTetra(ad, bd, cd, d, count); } } function render() { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); angle += 0.5; var modelViewMatrix = translate(0, 0, -2.5); modelViewMatrix = mult(modelViewMatrix, rotateX(angle)); modelViewMatrix = mult(modelViewMatrix, rotateY(angle * 0.7)); gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(modelViewMatrix)); gl.drawArrays(gl.TRIANGLES, 0, points.length); requestAnimationFrame(render); }
内容的提问来源于stack exchange,提问作者Durgesh Prajapati

