使球体在WebGL中旋转

时间:2016-04-17 03:31:38

标签: javascript rotation webgl

不确定我在这里缺少什么。尝试通过让用户点击“旋转”按钮来使行星(即球体)旋转,但似乎无法弄明白。我确实有以下段,通过用户与鼠标的交互来旋转球体:

    document.onmousemove = function() {
        if (!mouseDown) {
            return;
        }
        var newX = event.clientX;
        var newY = event.clientY;

        var deltaX = newX - lastMouseX
        var newRotationMatrix = mat4.create();
        mat4.identity(newRotationMatrix);
        mat4.rotate(newRotationMatrix, degToRad(deltaX / 10), [0, 1, 0]);

        var deltaY = newY - lastMouseY;
        mat4.rotate(newRotationMatrix, degToRad(deltaY / 10), [1, 0, 0]);

        mat4.multiply(newRotationMatrix, planetRotationMatrix, planetRotationMatrix);

        lastMouseX = newX
        lastMouseY = newY;
}

这个工作正常,但我也希望在用户点击按钮后让行星自动旋转。这是我的onLoad函数:

    window.onload = function onLoad() {
        canvas = document.getElementById("gl-canvas");
        initGL(canvas);
        initShaders();
        initBuffers();
        initTexture();
        initEvtHandlers();

        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);

        var newRotationMatrix = mat4.create();
        mat4.identity(newRotationMatrix);
        document.getElementById("rotate").onclick = function () {
            // rotation code goes here?
        }
        render(); 
}

我的问题是:用户点击按钮后如何“切换”旋转?到目前为止,我尝试过的是修改onmousemove函数,试图让球体在没有用户交互的情况下旋转,但这不起作用。

作为更新,这是我添加的新doRotate()函数:

var myRotationMatrix = mat4.create();
mat4.identity(myRotationMatrix);

doRotate = function () {
    var dx = 1;
    var newMatrix = mat4.create();
    mat4.identity(newMatrix);
    mat4.rotate(newMatrix, degToRad(dx / 10), [0, 1, 0]);

    mat4.multiply(newMatrix, myRotationMatrix);
    requestAnimFrame(doRotate);
}

目前不适用于我。单击按钮时,应调用此函数,切换旋转。

更新:

有关类似的演示,请参阅this page。我要做的是在用户点击相关按钮后自动旋转球体。这显然不在演示链接中 - 这只是为了澄清目的。

更新2:

我已经弄清楚了。有关详细信息,请参阅下面的答案。

2 个答案:

答案 0 :(得分:3)

我认为您doRotate的问题在于它不会重新绘制场景。当然,你计算了一个新的旋转矩阵,但你没有将信息传递给gpu,也没有告诉它再次绘制场景。但是,如果不知道你的程序是如何编写的,很难说这实际上是不是问题。

通常围绕mainLoop函数构建程序是一个好主意,该函数运行每一帧并处理所有必要的更新并重绘场景。像这样:

var lastTimeStamp = 0;

function mainLoop(timeStamp){
    var dt = timeStamp - lastTimeStamp;
    lastTimeStamp = timeStamp;

    everythingThatNeedsUpdate.doUpdate(dt); // this should calculate the new rotationMatrix for your sphere and send it to GPU

    gl.drawEverything();

    requestAnimationFrame(mainLoop);
}

然后,例如,*.doUpdate可以像这样实现:

*.doUpdate = function(dt){
    if (clickedOnSphere){
        rotationMatrix = doCalculations(dt);
        // send or flag rotationMatrix to be sent to GPU; 
    }
}

答案 1 :(得分:3)

经过无数次的反复试验后,我自己想出了。基本上,正如 WaclawJapser 提到的那样,我并没有重新绘制场景。我也完全无视制服。我不会发布整个代码,但这是修复后的相关函数:

requestAnimationFrame(rotatePlanet);

    function rotatePlanet(now) {
        if (flag == false) {
            return; // do nothing if unchecked
        } else {
            // Start by clearing the current buffers to avoid an overlap.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            // Subtract the previous time from the current time.
            now *= 0.001;
            var deltaTime = now - then;
            // Cache the current time to remember it for the next frame.
            then = now;

            // Every frame increases the rotation a little.
            rotation[0] += rotationSpeed * deltaTime;

            // Perform the necessary transformations. 
            mat4.identity(mvMatrix);
            mat4.translate(mvMatrix, [0, 0, -6]);
            mat4.rotate(mvMatrix, rotation[0], [0, 1, 0]); // rotates around y
            mat4.rotate(mvMatrix, rotation[1], [1, 0, 0]); // rotates around x

            // Set the matrix.
            setMatrixUniforms();

            // Draw the geometry.
            gl.drawElements(gl.TRIANGLES, planetVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

            requestAnimationFrame(rotatePlanet);
        }
    }

这现在完全按照我原先的意图行事。我不知道它是否会对任何人有所帮助,因为它似乎是一个非常狭窄的问题,但我想我会发布状态更新。

感谢所有尝试过帮助的人!