简体   繁体   中英

Making a Sphere Rotate in WebGL

Not sure what I'm missing here. Trying to make a planet (ie sphere) rotate by having the user click on a 'Rotate' button, but can't seem to figure it out. I do have the following segment which rotates the sphere by way of user interaction with the mouse:

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

This one works fine, but I also want to make the planet rotate automatically after the user clicks the button. Here is my onLoad function:

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

My question is: How can I 'toggle' the rotation after the user clicks the button? So far what I've tried is modify the onmousemove function in an attempt to make the sphere rotate without user interaction, but that's not working.

As an update, here is a new doRotate() function I added:

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

Which is currently not working for me. This function is supposed to be called when the button is clicked, toggling the rotation.

Update:

For a similar demo, please see this page . What I'm trying to do is make the sphere rotate automatically after the user clicks the relevant button. This is obviously not in the demo linked--that's only for clarification purposes.

Update 2:

I've since figured it out. Please see my answer below for details.

I think the problem with your doRotate is that it doesnt re-draw the scene. Sure, you calculated a new rotation matrix but you have not pass the information to the gpu nor tell it to draw the scene again. But without knowing how your program is written it is hard to say if that is actually the problem.

It is usually a good idea to structure the program around a mainLoop function that runs every frame and handles all the necessary updates and redraw the scene. Like so:

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

Then, for example, *.doUpdate may be implemented like such:

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

I figured it out on my own after countless amounts of trial and error. Basically, as WaclawJapser alluded to, I was not redrawing the scene. I was also ignoring the uniforms altogether. I won't post the whole code, but here is the relevant function after the fix:

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

This now works exactly as I had originally intended. I don't know if it will help anyone since it seems like quite a narrow issue, but I figure I'd post a status update.

Thanks to everyone who has tried to help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM