在WebGL中使一个球体旋转

6

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

    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);
}

当前这个功能对我来说不可用。当点击按钮时,应该调用此函数,切换旋转。

更新:

有一个类似的示例,请参见此页面。我正在尝试在用户点击相关按钮后自动使球体旋转。这显然不在链接的演示中 - 这只是为了澄清目的。

更新2:

我已经解决了问题。请参阅下面的答案获取详细信息。


1
我不太确定你在问什么。如果你想让它保持旋转,你需要使用requestAnimationFrame来创建某种动画循环,并且每次在循环中都稍微旋转一下行星。示例 - gman
有趣的问题。如果您能够组合一个示例,例如使用codepen或jsfiddle,那将会很有帮助。这样您就可以更好地演示您的问题,并且很可能更快地得到答案。 - Ben Smith
或者,你可以比 CodePen / JSFiddle 更好地将其直接嵌入到你的问题中(https://blog.stackoverflow.com/2014/09/introducing-runnable-javascript-css-and-html-code-snippets/)。只需单击编辑问题区域上方工具栏中带有`<>`的小文档图标即可。 - gman
@gman 不错。我也需要开始使用它! - Ben Smith
@gman 感谢您的建议。实际上我已经尝试过了,但是我无法让纹理正常工作,所以我决定忽略它,并发布相关代码片段。值得一提的是,这个想法类似于这个演示: http://learningwebgl.com/lessons/lesson11/index.html现在想象一下,如果有一个按钮可以使球体自动旋转,那么这基本上就是我在这里想要做的。 - Fiery Phoenix
显示剩余5条评论
2个回答

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; 
    }
}

3

我通过无数次的试验和错误自己解决了这个问题。基本上,正如WaclawJapser所暗示的那样,我没有重新绘制场景。我也完全忽略了uniforms。我不会贴出整个代码,但是在修正后,以下是相关函数:

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);
        }
    }

现在这个功能已经完全按照我最初的意图工作了。虽然它似乎是一个比较狭窄的问题,但我认为我会发布一个状态更新来帮助任何需要的人。

感谢大家的帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接