tween.js 和 three.js 未对齐的平滑旋转

Misalligned smooth rotation with tween.js and three.js

使用 three.js 和 tween.js 我正在尝试创建魔方。它由 27 个小立方体组成,这些立方体组合在一起按 +Math.PI/2-Math.PI/2 旋转。补间库用于平滑旋转。

我的问题是,如果一张脸被旋转多次(超过 10 次),它会明显错位,我无法理解如何修复它(link 问题的视觉图像)。

这是下例中旋转对象的函数:

function rotate( ii ) {
    // Creates a dummy object to group multiple objects together and
    // rotate (only one object in this example).
    var rotationGroup = new THREE.Object3D();
    scene.add(rotationGroup);
    THREE.SceneUtils.attach( box[ ii ], scene, rotationGroup );
    rotationGroup.updateMatrixWorld();

    // Checks that no other animations are active. If so stops the
    // function (this is rude, but it's not the focus of the example).
    if( TWEEN.getAll().length>0 )
        return;

    // Creates the tween object for animation.
    var tween;
    var duration = 300;
    var radiants = Math.PI/2;
    tween = new TWEEN.Tween( rotationGroup.rotation ).to( { z: radiants }, duration );
    tween.easing( TWEEN.Easing.Quadratic.InOut );
    tween.onComplete(
        function() {
            THREE.SceneUtils.detach( box[ ii ], rotationGroup, scene );
            box[ii].updateMatrixWorld();
            scene.remove(rotationGroup);
        }
    );

    // Starts the animation.
    tween.start();
}

我不知道如何在 Whosebug 上创建 运行 代码片段,所以现在我将粘贴一些代码来复制问题(不是 rubick 的代码,它更长更混乱)。

var camera, scene, renderer;
var canvas;

var cameraControls;
var clock = new THREE.Clock();

var box = [];

function main() {
    init();
    fillScene();
    addToDOM();
    animate();

    function init() {
        var canvasWidth = window.innerWidth;
        var canvasHeight = window.innerHeight;
        var canvasRatio = canvasWidth / canvasHeight;

        // RENDERER
        renderer = new THREE.WebGLRenderer( { antialias: true } );
        renderer.gammaInput = true;
        renderer.gammaOutput = true;
        renderer.setSize(canvasWidth, canvasHeight);
        renderer.setClearColor( 0xAAAAAA );

        // CAMERA
        camera = new THREE.PerspectiveCamera( 45, canvasRatio, 1, 1000 );
        camera.position.set(20,15,10);
        camera.lookAt( new THREE.Vector3(0,0,0) );

        // CONTROLS
        cameraControls = new THREE.OrbitControls(camera, renderer.domElement);

        document.onkeydown = function(ev) {onKeyDown(ev);};

    }

    function addToDOM() {
        var container = document.getElementById('WebGL-output');
        var canvas = container.getElementsByTagName('canvas');
        if (canvas.length>0) {
            container.removeChild(canvas[0]);
        }
        container.appendChild( renderer.domElement );
    }

    function animate() {
        requestAnimationFrame( animate );
        render();
    }

    function render() {
        var delta = clock.getDelta();
        cameraControls.update(delta);

        TWEEN.update();

        renderer.render(scene, camera);
    }

    function onKeyDown(ev) {
        switch( ev.keyCode) {

            case 65: // a
                rotate(0);
                break;
            case 83: // s
                rotate(1);
                break;
            case 68: // d
                rotate(2);
                break;

            default: break;
        }
    }

}

function rotate( ii ) {

    var rotationGroup = new THREE.Object3D();
    scene.add(rotationGroup);
    THREE.SceneUtils.attach( box[ ii ], scene, rotationGroup );
    rotationGroup.updateMatrixWorld();

    if( TWEEN.getAll().length>0 )
        return;
    var tween;
    var duration = 300;
    var radiants = Math.PI/2;

    tween = new TWEEN.Tween( rotationGroup.rotation ).to( { z: radiants }, duration );

    tween.easing( TWEEN.Easing.Quadratic.InOut );
    tween.onComplete(
        function() {
            THREE.SceneUtils.detach( box[ ii ], rotationGroup, scene );
            box[ii].updateMatrixWorld();
            scene.remove(rotationGroup);
        }
    );

    tween.start();
}

function fillScene() {
    scene = new THREE.Scene();
    scene.fog = new THREE.Fog( 0xAAAAAA, 2000, 4000 );

    var axisHelper = new THREE.AxisHelper( 20 );
    scene.add( axisHelper );

    // LIGHTS
    scene.add( new THREE.AmbientLight(0x333333) );
    var light = new THREE.PointLight( 0x333333, 2 );
    light.position.set(15,15,15);
    scene.add(light);
    light = new THREE.PointLight( 0x333333, 2 );
    light.position.set(-15,-15,-15);
    scene.add(light);

    var boxGeometry = new THREE.BoxGeometry( 10, 3, 10);
    var red   = new THREE.MeshPhongMaterial({ color: 0xff0000 });
    var blue  = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
    var green = new THREE.MeshPhongMaterial({ color: 0x0000ff });
    var black = new THREE.MeshPhongMaterial({ color: 0x000000 });
    var materials = [
        [ red, red, red, red, black, black ],
        [ green, green, green, green, black, black ],
        [ blue, blue, blue, blue, black, black ]
    ];
    for( var ii=0; ii<3; ii++ ) {
        box[ii] = new THREE.Mesh( boxGeometry, new THREE.MeshFaceMaterial(materials[ii]) );
        box[ii].rotation.x = Math.PI/2;
        box[ii].position.set(0,0,3-(ii*3));
        scene.add( box[ii] );
    }

}

在 js 控制台上打印 rotationGroupbox[ii] 的旋转似乎它们不匹配:Object3D 的旋转在第四个十进制数字上引入了一点错误.

我修改了补间完成后执行的匿名函数,添加了一些检查旋转并设置正确旋转值的代码(感谢@WestLangley 发现了解决方案)。

 function() {
     THREE.SceneUtils.detach( box[ ii ], rotationGroup, scene );
     box[ii].updateMatrixWorld();
     scene.remove(rotationGroup);

     // Checks the rotation value and sets the correct one accordingly
     var check = Math.floor( box[ii].rotation.z );
     if( check==1 )
         box[ii].rotation.z = Math.PI/2;
     else if( check==3 )
         box[ii].rotation.z = Math.PI;
     else if( check==-2 )
         box[ii].rotation.z = -Math.PI/2;
     else
         box[ii].rotation.z = 0;
 }