three.js 中的矩阵变换及两种旋转表达方式

Posted 可爱的黑精灵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了three.js 中的矩阵变换及两种旋转表达方式相关的知识,希望对你有一定的参考价值。

本篇简单介绍three.js中矩阵变换及两种旋转表达方式。

矩阵变换

three.js使用矩阵来保存Object3D的变换信息。

矩阵变换的基础

  • 平移变换

技术分享图片

  • 比例变换

技术分享图片

  • 旋转变换

(x,y,z,1)x轴旋转

技术分享图片

(x,y,z,1)y轴旋转

技术分享图片

(x,y,z,1)z轴旋转

技术分享图片

three.js中的矩阵


    var cube = new THREE.Mesh(new THREE.CubeGeometry(1,1,1),new THREE.MeshBasicMaterial());
        cube.position.set(1,2,3);
        cube.scale.set(7,8,9);

        scene.add(cube);

技术分享图片

我们可以看到正如上面的公式 cube的平移(1,2,3)所以elements[12]、elements[13]、elements[14]依次为1,2,3

cube的缩放为(7,8,9)所以elements[02]、elements[5]、elements[10]依次为7,8,9

然后我们选择一下cubex


    var cube = new THREE.Mesh(new THREE.CubeGeometry(1,1,1),new THREE.MeshBasicMaterial());
        cube.rotation.x = Math.PI/3;
        scene.add(cube);

技术分享图片

三维旋转表达方式

three.js提供了两种三维旋转表达方式:欧拉角(euler)四元数(quaternion)。它们相比较使用矩阵的方式进行变换更加的节省存储空间和更方便的进行插值。
但是欧拉角(euler)存在万向锁问题,配置可能失去一定的自由度所以都是使用在四元数(quaternion)

欧拉角

欧拉角需要指定x,y,z三个轴的角度和旋转的顺序。


    Euler( x, y, z, order )
    

万向锁问题:当三个万向节其中两个的轴发生重合时,会失去一个自由度的情形。

https://zh.wikipedia.org

技术分享图片

正常状态:三个独立的旋转轴

技术分享图片

万向锁:一旦选择±90°作为pitch角,就会导致第一次旋转和第三次旋转等价,整个旋转表示系统被限制在只能绕竖直轴旋转,丢失了一个表示维度。

四元数

四元数的出现就可以解决欧拉角的万向锁问题和万向锁带来的插值不是线性的问题。

具体的四元数在旋转的使用的原理可以参照:

维基百科—四元数与空间旋转


    Quaternion( x, y, z, w )
    

three.js中的旋转方式

从源码中我们可以看出,three.js都是使用quaternion(四元数)来控制旋转。


    setRotationFromAxisAngle: function ( axis, angle ) {

        // assumes axis is normalized

        this.quaternion.setFromAxisAngle( axis, angle );

    },

    setRotationFromEuler: function ( euler ) {

        this.quaternion.setFromEuler( euler, true );

    },

    setRotationFromMatrix: function ( m ) {

        // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

        this.quaternion.setFromRotationMatrix( m );

    },

    setRotationFromQuaternion: function ( q ) {

        // assumes q is normalized

        this.quaternion.copy( q );

    },

    rotateOnAxis: function () {

        // rotate object on axis in object space
        // axis is assumed to be normalized

        var q1 = new THREE.Quaternion();

        return function rotateOnAxis( axis, angle ) {

            q1.setFromAxisAngle( axis, angle );

            this.quaternion.multiply( q1 );

            return this;

        };

    }(),

object3D rotation 属性

three.js你可以使用rotation来设置object3D的旋转。

我们使用rotation设置的为一个Euler(欧拉角)所以它会先将Euler(欧拉角)转换为一个quaternion(四元数)

源码:


    rotation.onChange( onRotationChange );

    function onRotationChange() {

        quaternion.setFromEuler( rotation, false );

    }
    
    //setFromEuler()
    
        setFromEuler: function ( euler, update ) {
    
            if ( euler instanceof THREE.Euler === false ) {
    
                throw new Error( ‘THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.‘ );
    
            }
    
            // http://www.mathworks.com/matlabcentral/fileexchange/
            //  20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
            //  content/SpinCalc.m
    
            var c1 = Math.cos( euler._x / 2 );
            var c2 = Math.cos( euler._y / 2 );
            var c3 = Math.cos( euler._z / 2 );
            var s1 = Math.sin( euler._x / 2 );
            var s2 = Math.sin( euler._y / 2 );
            var s3 = Math.sin( euler._z / 2 );
    
            var order = euler.order;
    
            if ( order === ‘XYZ‘ ) {
    
                this._x = s1 * c2 * c3 + c1 * s2 * s3;
                this._y = c1 * s2 * c3 - s1 * c2 * s3;
                this._z = c1 * c2 * s3 + s1 * s2 * c3;
                this._w = c1 * c2 * c3 - s1 * s2 * s3;
    
            } else if ( order === ‘YXZ‘ ) {
    
                this._x = s1 * c2 * c3 + c1 * s2 * s3;
                this._y = c1 * s2 * c3 - s1 * c2 * s3;
                this._z = c1 * c2 * s3 - s1 * s2 * c3;
                this._w = c1 * c2 * c3 + s1 * s2 * s3;
    
            } else if ( order === ‘ZXY‘ ) {
    
                this._x = s1 * c2 * c3 - c1 * s2 * s3;
                this._y = c1 * s2 * c3 + s1 * c2 * s3;
                this._z = c1 * c2 * s3 + s1 * s2 * c3;
                this._w = c1 * c2 * c3 - s1 * s2 * s3;
    
            } else if ( order === ‘ZYX‘ ) {
    
                this._x = s1 * c2 * c3 - c1 * s2 * s3;
                this._y = c1 * s2 * c3 + s1 * c2 * s3;
                this._z = c1 * c2 * s3 - s1 * s2 * c3;
                this._w = c1 * c2 * c3 + s1 * s2 * s3;
    
            } else if ( order === ‘YZX‘ ) {
    
                this._x = s1 * c2 * c3 + c1 * s2 * s3;
                this._y = c1 * s2 * c3 + s1 * c2 * s3;
                this._z = c1 * c2 * s3 - s1 * s2 * c3;
                this._w = c1 * c2 * c3 - s1 * s2 * s3;
    
            } else if ( order === ‘XZY‘ ) {
    
                this._x = s1 * c2 * c3 - c1 * s2 * s3;
                this._y = c1 * s2 * c3 - s1 * c2 * s3;
                this._z = c1 * c2 * s3 + s1 * s2 * c3;
                this._w = c1 * c2 * c3 + s1 * s2 * s3;
    
            }
    
            if ( update !== false ) this.onChangeCallback();
    
            return this;
    
    
        },

Geometry rotateX方法

通过源码我们可以发现该方法使用的是矩阵变换的方式来旋转物体。


    rotateX: function () {

        // rotate geometry around world x-axis

        var m1;

        return function rotateX( angle ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeRotationX( angle );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    makeRotationX: function ( theta ) {

        var c = Math.cos( theta ), s = Math.sin( theta );

        this.set(

            1, 0,  0, 0,
            0, c, - s, 0,
            0, s,  c, 0,
            0, 0,  0, 1

        );

        return this;

    },

以上是关于three.js 中的矩阵变换及两种旋转表达方式的主要内容,如果未能解决你的问题,请参考以下文章

three.js中的矩阵变换(模型视图投影变换)

旋转矩阵

如何在 THREE.js 中找到两个向量之间的旋转矩阵

opencv 图像各方向旋转

three.js中物体旋转实践之房门的打开与关闭

Three.js坐标系与变换矩阵快速入门