美文网首页
three.js源码阅读笔记二

three.js源码阅读笔记二

作者: magicLiuyuan | 来源:发表于2019-10-15 21:13 被阅读0次

    本篇文章开始阅读three.js里面的Matrix4的实现。
    three.js的对象的位置、旋转、缩放等变换信息都在这个矩阵里,因此它还是很关键的。

    首先是一个Matrix4的构造函数

    /**
     * @author mrdoob / http://mrdoob.com/
     * @author supereggbert / http://www.paulbrunt.co.uk/
     * @author philogb / http://blog.thejit.org/
     * @author jordi_ros / http://plattsoft.com
     * @author D1plo1d / http://github.com/D1plo1d
     * @author alteredq / http://alteredqualia.com/
     * @author mikael emtinger / http://gomo.se/
     * @author timknip / http://www.floorplanner.com/
     * @author bhouston / http://clara.io
     * @author WestLangley / http://github.com/WestLangley
     */
    
    function Matrix4() {
    
        this.elements = [
    
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
    
        ];
    
        if ( arguments.length > 0 ) {
    
            console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
    
        }
    
    }
    
    

    从实现过程里可以看到,这个构造函数的数据属性只有elements一个属性,它初始化了一个长度为16的数组,用来表示一个4*4的单位矩阵。下面的参数长度判断是为了提示现在版本的初始化一个matrix对象不能传参了,默认就是单位矩阵,要想设置值,得用它下面定义的set()方法。

    下面它用Object.assign方法,将一个属性全是函数的对象复制到Matrix4的原型对象上,用来实现在Matrix4的原型上添加方法属性。

    第一个属性是用来判断用此构造函数构造出来的对象是否是Matrix4

    isMatrix4: true,
    

    下面就是给这个矩阵Matrix4赋值的函数

        set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
    
            var te = this.elements;
    
            te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
            te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
            te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
            te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
    
            return this;
    
        },
    
    

    这里需要按照先行后列的顺序进行传值,而有趣的是matrix4的elements属性却是按照先列后行的顺序依次存入到数组里面去的。也就是在matrix4的elements数组里,前4个值是第一列,后面4个是第二列,后面类推,这样做是方便它后面的取值赋值等计算。

    接下来是一个单位化该4*4矩阵的函数

        identity: function () {
    
            this.set(
    
                1, 0, 0, 0,
                0, 1, 0, 0,
                0, 0, 1, 0,
                0, 0, 0, 1
    
            );
    
            return this;
    
        },
    

    该函数将此matrix4设置为单位4*4矩阵。

    下面是一个克隆函数,即返回一个与此matrix4一模一样的值。

        clone: function () {
    
            return new Matrix4().fromArray( this.elements );
    
        },
    

    这里它上面用到了fromArray函数,这个函数是从传入的array里设置该matrix4的值。它的实现过程

        fromArray: function ( array, offset ) {
    
            if ( offset === undefined ) offset = 0;
    
            for ( var i = 0; i < 16; i ++ ) {
    
                this.elements[ i ] = array[ i + offset ];
    
            }
    
            return this;
    
        },
    

    可以看到它接收一个数组和一个偏移量参数,偏移量就是指从这个数组的哪一个位置开始是要设置的matrix4的值,偏移量开始后面是16个连续的值作为matrix4的值,默认偏移量是0。这里要注意array里面值的顺序是要按照先列后行的顺序来存储,因为elements数组里存储的矩阵顺序就是先列后行的顺序。

    然后接下来是一个复制函数,即该matrix4从传入的matrix4参数来复制,并且设置为自己的值。

        copy: function ( m ) {
    
            var te = this.elements;
            var me = m.elements;
    
            te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
            te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
            te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
            te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
    
            return this;
    
        },
    

    可以看到它就是将该matrix4的值的elements的值设置成和传入的matrix4的elements值一样。

    下面是一个只复制位置的函数

        copyPosition: function ( m ) {
    
            var te = this.elements, me = m.elements;
    
            te[ 12 ] = me[ 12 ];
            te[ 13 ] = me[ 13 ];
            te[ 14 ] = me[ 14 ];
    
            return this;
    
        },
    

    就是将传入参数matrix4的elements的倒数2,3,4个元素复制到该matrix4的对应位置,该位置的即44矩阵里的1行4列,2行4列,3行4列,的位置的值。那为什么这个位置数字表示位置呢?我们这里来分析一下。
    我们用4
    4矩阵来表示一个三维坐标下的点的平移旋转缩放信息(用4*4矩阵的原因后面分析),那么我们假设点P原来位置为(x,y,z),需要平移的量分别是(tx,ty,tz),那么平移后是x+tx,y+ty,z+tz,我们用矩阵来表示就是

    矩阵乘法

    计算一下这个矩阵,就会发现确实是x'=x+tx,y'=y+ty,z'=z+tz。

    接下来是从矩阵的值里取值x,y,z轴的基础值

        extractBasis: function ( xAxis, yAxis, zAxis ) {
    
            xAxis.setFromMatrixColumn( this, 0 );
            yAxis.setFromMatrixColumn( this, 1 );
            zAxis.setFromMatrixColumn( this, 2 );
    
            return this;
    
        },
    

    这里用到了Vector3的一个方法setFromMatrixColumn,那么这个方法我们也看一下

        setFromMatrixColumn: function ( m, index ) {
    
            return this.fromArray( m.elements, index * 4 );
    
        },
    

    可以看到它又调用了自己的fromArray方法

        fromArray: function ( array, offset ) {
    
            if ( offset === undefined ) offset = 0;
    
            this.x = array[ offset ];
            this.y = array[ offset + 1 ];
            this.z = array[ offset + 2 ];
    
            return this;
    
        },
    

    那么根据这几个函数,我们可以推测出,一开始的extractBasis方法,是将此matrix4的矩阵里elements数组的值,依次把它的第0,1,2个值赋值给传的vector3的x,y,z。后面依次是4,5,6赋值给yAxis,8,9,10赋值给zAxis。其实对应于矩阵就是该矩阵的第1列的1,2,3行是xAxis,第2列的1,2,3行是yAxis,第三列的1,2,3行是zAxis。


    extractBasis函数意义

    至于它代表的数学意义,现在我还有点不是很明白,后面再研究。

    接下来是一个与上面相反的函数,是通过传入的xAxis, yAxis, zAxis来将此matrix4的值的矩阵的第1列的1,2,3行设置成xAxis,第2列的1,2,3行设置成yAxis,第三列的1,2,3行设置成zAxis。

        makeBasis: function ( xAxis, yAxis, zAxis ) {
    
            this.set(
                xAxis.x, yAxis.x, zAxis.x, 0,
                xAxis.y, yAxis.y, zAxis.y, 0,
                xAxis.z, yAxis.z, zAxis.z, 0,
                0, 0, 0, 1
            );
    
            return this;
    
        },
    

    接下来是将传入的矩阵的旋转分量赋值给此矩阵的旋转分量

        extractRotation: function () {
    
            var v1 = new Vector3();
    
            return function extractRotation( m ) {
    
                // this method does not support reflection matrices
    
                var te = this.elements;
                var me = m.elements;
    
                var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
                var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
                var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
    
                te[ 0 ] = me[ 0 ] * scaleX;
                te[ 1 ] = me[ 1 ] * scaleX;
                te[ 2 ] = me[ 2 ] * scaleX;
                te[ 3 ] = 0;
    
                te[ 4 ] = me[ 4 ] * scaleY;
                te[ 5 ] = me[ 5 ] * scaleY;
                te[ 6 ] = me[ 6 ] * scaleY;
                te[ 7 ] = 0;
    
                te[ 8 ] = me[ 8 ] * scaleZ;
                te[ 9 ] = me[ 9 ] * scaleZ;
                te[ 10 ] = me[ 10 ] * scaleZ;
                te[ 11 ] = 0;
    
                te[ 12 ] = 0;
                te[ 13 ] = 0;
                te[ 14 ] = 0;
                te[ 15 ] = 1;
    
                return this;
    
            };
    
        }(),
    

    它的实现过程是首先拿到传入参数的matrix4对应的点xAxis, yAxis, zAxis的长度,然后将此matrix4的对应位置都除以此长度。

    接下来是将此矩阵通过给定的欧拉角进行旋转转换,将此矩阵的左上角的3*3的矩阵进行更新。

        makeRotationFromEuler: function ( euler ) {
    
            if ( ! ( euler && euler.isEuler ) ) {
    
                console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
    
            }
    
            var te = this.elements;
    
            var x = euler.x, y = euler.y, z = euler.z;
            var a = Math.cos( x ), b = Math.sin( x );
            var c = Math.cos( y ), d = Math.sin( y );
            var e = Math.cos( z ), f = Math.sin( z );
    
            if ( euler.order === 'XYZ' ) {
    
                var ae = a * e, af = a * f, be = b * e, bf = b * f;
    
                te[ 0 ] = c * e;
                te[ 4 ] = - c * f;
                te[ 8 ] = d;
    
                te[ 1 ] = af + be * d;
                te[ 5 ] = ae - bf * d;
                te[ 9 ] = - b * c;
    
                te[ 2 ] = bf - ae * d;
                te[ 6 ] = be + af * d;
                te[ 10 ] = a * c;
    
            } else if ( euler.order === 'YXZ' ) {
    
                var ce = c * e, cf = c * f, de = d * e, df = d * f;
    
                te[ 0 ] = ce + df * b;
                te[ 4 ] = de * b - cf;
                te[ 8 ] = a * d;
    
                te[ 1 ] = a * f;
                te[ 5 ] = a * e;
                te[ 9 ] = - b;
    
                te[ 2 ] = cf * b - de;
                te[ 6 ] = df + ce * b;
                te[ 10 ] = a * c;
    
            } else if ( euler.order === 'ZXY' ) {
    
                var ce = c * e, cf = c * f, de = d * e, df = d * f;
    
                te[ 0 ] = ce - df * b;
                te[ 4 ] = - a * f;
                te[ 8 ] = de + cf * b;
    
                te[ 1 ] = cf + de * b;
                te[ 5 ] = a * e;
                te[ 9 ] = df - ce * b;
    
                te[ 2 ] = - a * d;
                te[ 6 ] = b;
                te[ 10 ] = a * c;
    
            } else if ( euler.order === 'ZYX' ) {
    
                var ae = a * e, af = a * f, be = b * e, bf = b * f;
    
                te[ 0 ] = c * e;
                te[ 4 ] = be * d - af;
                te[ 8 ] = ae * d + bf;
    
                te[ 1 ] = c * f;
                te[ 5 ] = bf * d + ae;
                te[ 9 ] = af * d - be;
    
                te[ 2 ] = - d;
                te[ 6 ] = b * c;
                te[ 10 ] = a * c;
    
            } else if ( euler.order === 'YZX' ) {
    
                var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
    
                te[ 0 ] = c * e;
                te[ 4 ] = bd - ac * f;
                te[ 8 ] = bc * f + ad;
    
                te[ 1 ] = f;
                te[ 5 ] = a * e;
                te[ 9 ] = - b * e;
    
                te[ 2 ] = - d * e;
                te[ 6 ] = ad * f + bc;
                te[ 10 ] = ac - bd * f;
    
            } else if ( euler.order === 'XZY' ) {
    
                var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
    
                te[ 0 ] = c * e;
                te[ 4 ] = - f;
                te[ 8 ] = d * e;
    
                te[ 1 ] = ac * f + bd;
                te[ 5 ] = a * e;
                te[ 9 ] = ad * f - bc;
    
                te[ 2 ] = bc * f - ad;
                te[ 6 ] = b * e;
                te[ 10 ] = bd * f + ac;
    
            }
    
            // bottom row
            te[ 3 ] = 0;
            te[ 7 ] = 0;
            te[ 11 ] = 0;
    
            // last column
            te[ 12 ] = 0;
            te[ 13 ] = 0;
            te[ 14 ] = 0;
            te[ 15 ] = 1;
    
            return this;
    
        },
    
    

    详细推理请看 维基百科 欧拉角, 关于欧拉角已经万向节死锁,还有另一篇博文参考万向节死锁,不过此处的实现正是给定了12种情况,在这12种情况里,是不会发生万向节死锁的问题的。

    接下来是通过四元数进行旋转

        makeRotationFromQuaternion: function () {
    
            var zero = new Vector3( 0, 0, 0 );
            var one = new Vector3( 1, 1, 1 );
    
            return function makeRotationFromQuaternion( q ) {
    
                return this.compose( zero, q, one );
    
            };
    
        }(),
    

    可以看到它是将Vector3( 0, 0, 0 ), q, Vector3( 1, 1, 1 )作为参数传入compose函数,那么我们看一下compose函数是什么

    compose: function ( position, quaternion, scale ) {
    
            var te = this.elements;
    
            var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
            var x2 = x + x, y2 = y + y, z2 = z + z;
            var xx = x * x2, xy = x * y2, xz = x * z2;
            var yy = y * y2, yz = y * z2, zz = z * z2;
            var wx = w * x2, wy = w * y2, wz = w * z2;
    
            var sx = scale.x, sy = scale.y, sz = scale.z;
    
                te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
                te[ 1 ] = ( xy + wz ) * sx;
                te[ 2 ] = ( xz - wy ) * sx;
                te[ 3 ] = 0;
    
                te[ 4 ] = ( xy - wz ) * sy;
                te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
                te[ 6 ] = ( yz + wx ) * sy;
                te[ 7 ] = 0;
    
                te[ 8 ] = ( xz + wy ) * sz;
                te[ 9 ] = ( yz - wx ) * sz;
                te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
                te[ 11 ] = 0;
    
                te[ 12 ] = position.x;
                te[ 13 ] = position.y;
                te[ 14 ] = position.z;
                te[ 15 ] = 1;
    
                return this;
    
        },
    

    此方法是传入位置、旋转、缩放参数,然后根据这三个参数计算更新后的矩阵变换。其中旋转除了上面的欧拉角可以定义,还可以用四元数表示。关于四元数的介绍,可以参考 如何形象的理解四元数

    接下来是三个按照指定轴旋转特定角度的matrix4的更新

        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;
    
        },
    
        makeRotationY: function ( theta ) {
    
            var c = Math.cos( theta ), s = Math.sin( theta );
    
            this.set(
    
                 c, 0, s, 0,
                 0, 1, 0, 0,
                - s, 0, c, 0,
                 0, 0, 0, 1
    
            );
    
            return this;
    
        },
    
        makeRotationZ: function ( theta ) {
    
            var c = Math.cos( theta ), s = Math.sin( theta );
    
            this.set(
    
                c, - s, 0, 0,
                s, c, 0, 0,
                0, 0, 1, 0,
                0, 0, 0, 1
    
            );
    
            return this;
    
        },
    

    这里的数学推导可以自己去推算一下。
    下面是一个缩放的函数,将此矩阵按照x,y,z的值进行缩放计算

        makeScale: function ( x, y, z ) {
    
            this.set(
    
                x, 0, 0, 0,
                0, y, 0, 0,
                0, 0, z, 0,
                0, 0, 0, 1
    
            );
    
            return this;
    
        },
    

    下面是一个剪切面计算,就是按照x,y,z轴上的裁剪量进行裁剪计算

        makeShear: function ( x, y, z ) {
    
            this.set(
    
                1, y, z, 0,
                x, 1, z, 0,
                x, y, 1, 0,
                0, 0, 0, 1
    
            );
    
            return this;
    
        },
    

    下面这个方法是将此matrix4的位置、旋转、缩放分量赋值给传入的对应参数

        decompose: function () {
    
            var vector = new Vector3();
            var matrix = new Matrix4();
    
            return function decompose( position, quaternion, scale ) {
    
                var te = this.elements;
    
                var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
                var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
                var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
    
                // if determine is negative, we need to invert one scale
                var det = this.determinant();
                if ( det < 0 ) sx = - sx;
    
                position.x = te[ 12 ];
                position.y = te[ 13 ];
                position.z = te[ 14 ];
    
                // scale the rotation part
                matrix.copy( this );
    
                var invSX = 1 / sx;
                var invSY = 1 / sy;
                var invSZ = 1 / sz;
    
                matrix.elements[ 0 ] *= invSX;
                matrix.elements[ 1 ] *= invSX;
                matrix.elements[ 2 ] *= invSX;
    
                matrix.elements[ 4 ] *= invSY;
                matrix.elements[ 5 ] *= invSY;
                matrix.elements[ 6 ] *= invSY;
    
                matrix.elements[ 8 ] *= invSZ;
                matrix.elements[ 9 ] *= invSZ;
                matrix.elements[ 10 ] *= invSZ;
    
                quaternion.setFromRotationMatrix( matrix );
    
                scale.x = sx;
                scale.y = sy;
                scale.z = sz;
    
                return this;
    
            };
    
        }(),
    

    下面是两个关于相机的方法,这两个方法分别由透视相机和正交相机的内部更新相机矩阵使用

    makePerspective: function ( left, right, top, bottom, near, far ) {
    
            if ( far === undefined ) {
    
                console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
    
            }
    
            var te = this.elements;
            var x = 2 * near / ( right - left );
            var y = 2 * near / ( top - bottom );
    
            var a = ( right + left ) / ( right - left );
            var b = ( top + bottom ) / ( top - bottom );
            var c = - ( far + near ) / ( far - near );
            var d = - 2 * far * near / ( far - near );
    
            te[ 0 ] = x;    te[ 4 ] = 0;    te[ 8 ] = a;    te[ 12 ] = 0;
            te[ 1 ] = 0;    te[ 5 ] = y;    te[ 9 ] = b;    te[ 13 ] = 0;
            te[ 2 ] = 0;    te[ 6 ] = 0;    te[ 10 ] = c;   te[ 14 ] = d;
            te[ 3 ] = 0;    te[ 7 ] = 0;    te[ 11 ] = - 1; te[ 15 ] = 0;
    
            return this;
    
        },
    
        makeOrthographic: function ( left, right, top, bottom, near, far ) {
    
            var te = this.elements;
            var w = 1.0 / ( right - left );
            var h = 1.0 / ( top - bottom );
            var p = 1.0 / ( far - near );
    
            var x = ( right + left ) * w;
            var y = ( top + bottom ) * h;
            var z = ( far + near ) * p;
    
            te[ 0 ] = 2 * w;    te[ 4 ] = 0;    te[ 8 ] = 0;    te[ 12 ] = - x;
            te[ 1 ] = 0;    te[ 5 ] = 2 * h;    te[ 9 ] = 0;    te[ 13 ] = - y;
            te[ 2 ] = 0;    te[ 6 ] = 0;    te[ 10 ] = - 2 * p; te[ 14 ] = - z;
            te[ 3 ] = 0;    te[ 7 ] = 0;    te[ 11 ] = 0;   te[ 15 ] = 1;
    
            return this;
    
        },
    

    接下来是判定给定的matrix4与此matrix4是否相等的方法

    equals: function ( matrix ) {
    
            var te = this.elements;
            var me = matrix.elements;
    
            for ( var i = 0; i < 16; i ++ ) {
    
                if ( te[ i ] !== me[ i ] ) return false;
    
            }
    
            return true;
    
        },
    

    可以看到它的对比过程是直接比对元素elements里面的每一个元素是否一样。

    然后是将矩阵的值设置为传入的数组里的值的方法,即将此矩阵的元素赋值给定的数组里。

        toArray: function ( array, offset ) {
    
            if ( array === undefined ) array = [];
            if ( offset === undefined ) offset = 0;
    
            var te = this.elements;
    
            array[ offset ] = te[ 0 ];
            array[ offset + 1 ] = te[ 1 ];
            array[ offset + 2 ] = te[ 2 ];
            array[ offset + 3 ] = te[ 3 ];
    
            array[ offset + 4 ] = te[ 4 ];
            array[ offset + 5 ] = te[ 5 ];
            array[ offset + 6 ] = te[ 6 ];
            array[ offset + 7 ] = te[ 7 ];
    
            array[ offset + 8 ] = te[ 8 ];
            array[ offset + 9 ] = te[ 9 ];
            array[ offset + 10 ] = te[ 10 ];
            array[ offset + 11 ] = te[ 11 ];
    
            array[ offset + 12 ] = te[ 12 ];
            array[ offset + 13 ] = te[ 13 ];
            array[ offset + 14 ] = te[ 14 ];
            array[ offset + 15 ] = te[ 15 ];
    
            return array;
    
        }
    

    后面还有一个构造一个旋转矩阵的方法,将该矩阵变为从传入的三个参数eye, center,up,里,从eye的位置去看center的位置,方向是up。

        lookAt: function () {
    
            var x = new Vector3();
            var y = new Vector3();
            var z = new Vector3();
    
            return function lookAt( eye, target, up ) {
    
                var te = this.elements;
    
                z.subVectors( eye, target );
    
                if ( z.lengthSq() === 0 ) {
    
                    // eye and target are in the same position
    
                    z.z = 1;
    
                }
    
                z.normalize();
                x.crossVectors( up, z );
    
                if ( x.lengthSq() === 0 ) {
    
                    // up and z are parallel
    
                    if ( Math.abs( up.z ) === 1 ) {
    
                        z.x += 0.0001;
    
                    } else {
    
                        z.z += 0.0001;
    
                    }
    
                    z.normalize();
                    x.crossVectors( up, z );
    
                }
    
                x.normalize();
                y.crossVectors( z, x );
    
                te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
                te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
                te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
    
                return this;
    
            };
    
        }(),
    

    然后是矩阵与矩阵之间的运算的方法

        multiply: function ( m, n ) {
    
            if ( n !== undefined ) {
    
                console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
                return this.multiplyMatrices( m, n );
    
            }
    
            return this.multiplyMatrices( this, m );
    
        },
    

    该函数是返回此矩阵左乘传入的m矩阵,并且将结果赋值给此矩阵,它的实现是调用了下面的这个矩阵的左乘函数

        multiplyMatrices: function ( a, b ) {
    
            var ae = a.elements;
            var be = b.elements;
            var te = this.elements;
    
            var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
            var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
            var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
            var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
    
            var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
            var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
            var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
            var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
    
            te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
            te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
            te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
            te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
    
            te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
            te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
            te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
            te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
    
            te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
            te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
            te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
            te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
    
            te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
            te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
            te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
            te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
    
            return this;
    
        },
    

    此方法是将接受的两个参数矩阵a,b,用a左乘b,然后将结果赋值给此矩阵。
    下面是一个参数左乘此矩阵并把结果赋值给此矩阵的方法

        premultiply: function ( m ) {
    
            return this.multiplyMatrices( m, this );
    
        },
    

    下面是将矩阵里的每一个值都缩放传入的参数的倍数

        multiplyScalar: function ( s ) {
    
            var te = this.elements;
    
            te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
            te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
            te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
            te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
    
            return this;
    
        },
    

    接下来的这个方法是传入一个BufferAtteibute属性,然后将此矩阵应用与此属性参数里的每一个3d向量

        applyToBufferAttribute: function () {
    
            var v1 = new Vector3();
    
            return function applyToBufferAttribute( attribute ) {
    
                for ( var i = 0, l = attribute.count; i < l; i ++ ) {
    
                    v1.x = attribute.getX( i );
                    v1.y = attribute.getY( i );
                    v1.z = attribute.getZ( i );
    
                    v1.applyMatrix4( this );
    
                    attribute.setXYZ( i, v1.x, v1.y, v1.z );
    
                }
    
                return attribute;
    
            };
    
        }(),
    

    实现过程就是获取参数BufferAtteibute里的每一个顶点的x,y,z,然后将此点做此矩阵的转换,最后再将结果设置回BufferAtteibute对应的位置的点。

    下面这个函数返回此矩阵的行列式

        determinant: function () {
    
            var te = this.elements;
    
            var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
            var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
            var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
            var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
    
            //TODO: make this more efficient
            //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
    
            return (
                n41 * (
                    + n14 * n23 * n32
                     - n13 * n24 * n32
                     - n14 * n22 * n33
                     + n12 * n24 * n33
                     + n13 * n22 * n34
                     - n12 * n23 * n34
                ) +
                n42 * (
                    + n11 * n23 * n34
                     - n11 * n24 * n33
                     + n14 * n21 * n33
                     - n13 * n21 * n34
                     + n13 * n24 * n31
                     - n14 * n23 * n31
                ) +
                n43 * (
                    + n11 * n24 * n32
                     - n11 * n22 * n34
                     - n14 * n21 * n32
                     + n12 * n21 * n34
                     + n14 * n22 * n31
                     - n12 * n24 * n31
                ) +
                n44 * (
                    - n13 * n22 * n31
                     - n11 * n23 * n32
                     + n11 * n22 * n33
                     + n13 * n21 * n32
                     - n12 * n21 * n33
                     + n12 * n23 * n31
                )
    
            );
    
        },
    
    

    下面这个方法是转置该矩阵

        transpose: function () {
    
            var te = this.elements;
            var tmp;
    
            tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
            tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
            tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
    
            tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
            tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
            tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
    
            return this;
    
        },
    
    

    接下来是传入一个vector3,根据此vector3的值更新此矩阵的位置

        setPosition: function ( v ) {
    
            var te = this.elements;
    
            te[ 12 ] = v.x;
            te[ 13 ] = v.y;
            te[ 14 ] = v.z;
    
            return this;
    
        },
    

    下面这个方式是将此矩阵设置为传入的参数矩阵的逆矩阵,如果传入第二个参数为true,则在第一个参数不可逆的时候抛出一个错误,否则就将此矩阵设置为4*4的单位矩阵

        getInverse: function ( m, throwOnDegenerate ) {
    
            // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
            var te = this.elements,
                me = m.elements,
    
                n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
                n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
                n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
                n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
    
                t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
                t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
                t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
                t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
    
            var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
    
            if ( det === 0 ) {
    
                var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0";
    
                if ( throwOnDegenerate === true ) {
    
                    throw new Error( msg );
    
                } else {
    
                    console.warn( msg );
    
                }
    
                return this.identity();
    
            }
    
            var detInv = 1 / det;
    
            te[ 0 ] = t11 * detInv;
            te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
            te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
            te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
    
            te[ 4 ] = t12 * detInv;
            te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
            te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
            te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
    
            te[ 8 ] = t13 * detInv;
            te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
            te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
            te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
    
            te[ 12 ] = t14 * detInv;
            te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
            te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
            te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
    
            return this;
    
        },
    
    

    下面这个scale方法接收一个vector3作为参数,并且将此矩阵的第一列乘以该vector3的x,第二列乘以y,第三列乘以z。

        scale: function ( v ) {
    
            var te = this.elements;
            var x = v.x, y = v.y, z = v.z;
    
            te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
            te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
            te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
            te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
    
            return this;
    
        },
    

    下面这个方法是返回此矩阵在x,y,z轴上的最大的值

        getMaxScaleOnAxis: function () {
    
            var te = this.elements;
    
            var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
            var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
            var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
    
            return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
    
        },
    
    

    最后这个方法是接受x,y,z,将此矩阵的位置属性更新为传入的这三个值

        makeTranslation: function ( x, y, z ) {
    
            this.set(
    
                1, 0, 0, x,
                0, 1, 0, y,
                0, 0, 1, z,
                0, 0, 0, 1
    
            );
    
            return this;
    
        },
    

    矩阵的属性方法就分析完了,基本都是44矩阵的一些数学运算。关于44矩阵具体代表一个点的什么样的位置变换,后面找机会再做一次专门的分析。

    相关文章

      网友评论

          本文标题:three.js源码阅读笔记二

          本文链接:https://www.haomeiwen.com/subject/rwafmctx.html