You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现3D谢尔宾斯基三角旋转?附WebGL代码求助

Fixing 3D Sierpinski Triangle Rotation

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 your MV.js library) 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.5 and 0.7 values) 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:42:14