Three.js source code reading

Posted by slshmily on Thu, 27 Jan 2022 11:35:58 +0100

API documentation: https://threejs.org/docs/index.html

The following source code comes from: three js v0. one hundred and thirty-five

How to view the source code

Create a blank npm project. After npm install three is executed in the project, select the node in the project_ Three is in the modules / three directory JS code.

build directory: packaged three JS code. Of which:

  • three.js: three. JS script version of JS
  • three.module.js: three. ES Module version of JS

src Directory: three JS source code

It is recommended to be familiar with the basic functions and usage of the class through the API document, and then read the source code of the class.

Basic tools

Vector2

Two dimensional vector. It can be used to express many things, such as:

  • A point on a plane: (x,y)
  • Direction and length of a in the plane: from (0,0) to (x,y)

Basic usage:

const a = new THREE.Vector2( 0, 1 );
const b = new THREE.Vector2( );//no arguments; will be initialised to (0, 0)
const d = a.distanceTo( b );

Source code overview:

class Vector2 {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }

    // ...
}

Vector3

Three dimensional vector. It can be used to express many things, such as:

  • A point in space: (x,y,z)
  • A direction and length in space: from (0,0,0) to (x,y,z)

Basic usage:

const a = new THREE.Vector3( 0, 1, 0 );
const b = new THREE.Vector3( );//no arguments; will be initialised to (0, 0, 0)
const d = a.distanceTo( b );

Vector4

Four dimensional vector. It is often used to represent homogeneous coordinates.

Source code overview:

class Vector4 {
    constructor( x = 0, y = 0, z = 0, w = 1 ) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }

    // ...
}

Quaternion

Quaternion, in three JS is used to represent rotation.

Basic usage:

const quaternion = new THREE.Quaternion();
// setFromAxisAngle(axis, angle)
quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 2 );

const vector = new THREE.Vector3( 1, 0, 0 );
vector.applyQuaternion( quaternion );

Matrix3

Three dimensional matrix.

Basic usage:

const m = new Matrix3(); // Create an identity matrix

Source code overview:

class Matrix3 {
    constructor() {
        this.elements = [
            1, 0, 0,
            0, 1, 0,
            0, 0, 1
        ];
    }

    // Current matrix = current matrix multiplied by matrix
    multiply(matrix) {...}
    // Current matrix = matrix times current matrix
    premultiply(matrix) {...}
    // Current matrix = inverse of current matrix
    invert(){...}
    // Current matrix = inverse transpose matrix of 3rd order matrix in the upper left corner of 4-dimensional matrix matrix4
    getNormalMatrix(matrix4){}
    // Current matrix = identity matrix of current matrix
    identity(){...}
    // Current matrix = transpose matrix of current matrix
    transpose(){...}
}

Note the order of row priority and column priority:

The set() method parameters adopt row major, and they are internally stored in the array in column major order, which means:

m.set( 11, 12, 13,
       21, 22, 23,
       31, 32, 33 );

The element array elements will be stored as:

m.elements = [ 11, 21, 31,
              12, 22, 32,
              13, 23, 33 ];

Internally, all calculations are performed using column precedence.

Matrix4

4-dimensional matrix. The most common use of a 4x4 matrix is as a transformation matrix.

Any 3D object has three associated matrices:

  • Object3D.matrix: local transformation matrix of the object (also known as local transformation matrix. Transformation matrix, that is, model matrix). This is the transformation matrix of the object relative to its parent object.

  • Object3D.matrixWorld: the global transformation matrix (or world transformation matrix) of the object. If the object has no parent object, it is the same as its local transformation matrix. (personal understanding: for example, there is an object B in object A, and object A has no parent object. The matrix of object A is translated to the left by 30m, while the matrix of object B is translated to the left by 10m. Then the matrixWorld of object A is translated to the left by 30m, and the matrixWorld of object B is translated to the left by 40m)

  • Object3D.modelViewMatrix: represents the transformation matrix of the object relative to the camera coordinate system (in other words, through this matrix, the object can be transformed into the camera coordinate system, which is the same as the definition of modelViewMatrix in WebGL). The modelViewMatrix of an object is equal to the inverse matrix of the camera's world transformation matrix multiplied by the object's world transformation matrix. (personal understanding: because of the relative relationship between the object and the camera, the inverse matrix of the matrixWorld of the camera should be equivalent to the viewMatrix of the object, and the matrixWorld of the object is actually the modelMatrix of the object, so the modelViewMatrix of the object is equal to the multiplication of the two)

Camera camera is a subclass of Object3D. In addition to the above matrices, it has several additional four-dimensional matrices:

  • Camera.projectionMatrix: projection matrix.
  • Camera. matrixWorld inverse: view matrix, which is the inverse matrix of matrixWorld of Camara.

Review the three matrices of the Web GL system: the matrixWorld of the object is modelMatrix, camera Matrixworldinverse is the viewMatrix, and camera The projectionMatrix is the projectionMatrix.

There are several options for extracting position (translation), rotation, and scaling from Matrix4.

  • Vector3.setFromMatrixPosition: extract position related components.
  • Vector3.setFromMatrixScale: extract scaling related components.
  • Quaternion.setFromRotationMatrix,Euler. Set from rotation matrix or extractRotation: extract rotation related components from a pure (non scaled) matrix.
  • decompose: can be used to extract position, rotation and scaling components at one time.

Object3D class

Object3D

This is three The base class of most objects in JS provides a series of attributes and methods to manipulate objects in three-dimensional space.

Source code overview:

let _object3DId = 0;

class Object3D extends EventDispatcher {
    constructor() {
        super();

        Object.defineProperty(this, 'id', { value: _object3DId++ });

        this.type = 'Object3D';

        this.parent = null;
        this.children = []; // One Object3D can contain multiple sub Object3D

        Object.defineProperties(this, {
            // Local position (translation. Initial position: (0,0,0))
            position: {
                configurable: true,
                enumerable: true,
                value: new Vector3()
            },
            // Local rotation
            // Quaternions are used in three.js to represent rotations
            quaternion: {
                configurable: true,
                enumerable: true,
                value: new Quaternion()
            },
            // Local scaling
            scale: {
                configurable: true,
                enumerable: true,
                value: new Vector3(1, 1, 1)
            },
            // modelViewMatrix
            // It is equal to the inverse matrix of the world model matrix of the camera multiplied by the world model matrix of the object. See the explanation in Matrix4 for details
            modelViewMatrix: {
                value: new Matrix4()
            },
            // "Magic matrix" for transforming vertex normal vector, inverse transpose matrix of modelViewMatrix. Refer to "WebGL programming guide abstract. md"
            normalMatrix: {
                value: new Matrix3()
            }
        });

        // Local model matrix
        // Contains this position, this. quaternion, this. Scale information, i.e. position (translation), rotation and zoom
        this.matrix = new Matrix4();
        // World model matrix
        this.matrixWorld = new Matrix4();

        // ...
    }

    // According to this position, this. quaternion, this. Scale update this matrix
    updateMatrix() {
        // By this position, this. quaternion, this. Scale generates this matrix
        this.matrix.compose(this.position, this.quaternion, this.scale);
        this.matrixWorldNeedsUpdate = true;
    }
    // According to this Matrix update this matrixWorld and update matrixWorld for all child ren
    updateMatrixWorld(force) { }

    // Local rotation
    rotateOnAxis(axis, angle) { }
    // Local Translation 
    translateOnAxis(axis, distance) { }
    // Local rotation, local translation and local scaling are carried out through a matrix
    applyMatrix4(matrix) {
        this.matrix.premultiply(matrix);
        // Put this This is extracted from the position, rotation and scaling components in the matrix position, this. quaternion, this. In scale
        this.matrix.decompose(this.position, this.quaternion, this.scale);
    }

    // Add / delete sub Object3D
    add(object) { }
    remove(object) { }

    // ...
}

The translation, rotation and scaling methods of Object3D do not modify the position coordinates of vertices (Object3D and its subclasses do not store the position coordinates of vertices), but indirectly modify the local model matrix to realize translation, rotation and scaling.

Comparison: BufferGeometry and its subclasses store the position coordinates of each vertex. Their translation, rotation and scaling are realized by modifying the position coordinates of vertices.

Scene

Scene. This is where you place objects, lights and cameras.

Source code overview:

class Scene extends Object3D {
    constructor() {
        super();

        this.type = 'Scene';

        this.background = null;
        this.environment = null;
        this.fog = null;
    }

    // ...
}

Camera & PerspectiveCamera

Camera

Source code overview:

class Camera extends Object3D {
    constructor() {
        super();

        this.type = 'Camera';

        // The inverse matrix of the camera's world model matrix, that is, the view matrix, is explained in Matrix4 above
        this.matrixWorldInverse = new Matrix4();
        // Projection matrix
        this.projectionMatrix = new Matrix4();
    }

    // ...
}

PerspectiveCamera

Source code overview:

class PerspectiveCamera extends Camera {
    constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) {
        super();

        this.type = 'PerspectiveCamera';

        this.fov = fov;
        this.aspect = aspect;
        this.near = near;
        this.far = far;

        // Update the projection matrix according to the above parameters
        this.updateProjectionMatrix();
    }

    // ...
}

Mesh

Represents an object based on a triangular mesh.

Source code overview:

class Mesh extends Object3D {
    constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) {
        super();

        this.type = 'Mesh';

        this.geometry = geometry;
        this.material = material;

        // ...
    }

    // ...
}

Geometric class

BufferGeometry

Store the vertex information constituting a geometry, including vertex coordinates, vertex normal vector, vertex texture coordinates, vertex index, etc.

Basic usage:

const geometry = new THREE.BufferGeometry();
// Create a simple rectangle It consists of two triangles
const vertices = new Float32Array( [
    -1.0, -1.0,  1.0,
     1.0, -1.0,  1.0,
     1.0,  1.0,  1.0,

     1.0,  1.0,  1.0,
    -1.0,  1.0,  1.0,
    -1.0, -1.0,  1.0
] );
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
const mesh = new THREE.Mesh( geometry, material );

Source code overview:

let _id = 0;

class BufferGeometry extends EventDispatcher {
    constructor() {
        super();

        Object.defineProperty(this, 'id', { value: _id++ });

        this.type = 'BufferGeometry';

        // Vertex attributes, including vertex coordinates, vertex normal vector, vertex texture coordinates, vertex index, etc
        this.attributes = {};
    }

    // Apply matrix to vertex attributes such as vertex coordinates and vertex normal vector. The effects are rotation, translation and scaling
    applyMatrix4(matrix) { }
    // Rotate, translate and zoom. It is implemented internally by calling applyMatrix4
    rotateX(angle) { }
    rotateY(angle) { }
    rotateZ(angle) { }
    translate(x, y, z) { }
    scale(x, y, z) { }

    // Set vertex attributes (this.attributes[name])
    setAttribute( name, attribute ) {
        this.attributes[ name ] = attribute;
        return this;
    }
    // Set vertex coordinates (this.attributes['position '])
    setFromPoints(points) {
        const position = [];
        for ( let i = 0, l = points.length; i < l; i++ ) {
            const point = points[ i ];
            position.push( point.x, point.y, point.z || 0 );
        }
        this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
    }

    // ...
}

BoxGeometry

The specific geometry represented by BoxGeometry is a simple encapsulation of the basic geometry BufferGeometry.

BufferGeometry requires us to manually input various vertex attributes, while BoxGeometry and other specific geometry can automatically calculate various vertex attributes according to the parameters of the constructor.

Source code overview:

// Cube
class BoxGeometry extends BufferGeometry {
    // Parameters: width, height, depth, width split segment, height split segment, depth split segment
    constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
        super();

        this.type = 'BoxGeometry';

        const indices = []; // Vertex Index 
        const vertices = []; // Vertex coordinates
        const normals = []; // Vertex normal vector
        const uvs = []; // Vertex texture coordinates

        // According to the constructor parameters, calculate the vertex coordinates, vertex normal vector, vertex texture coordinates and vertex index of each face of the cube
        // build each side of the box geometry
        buildPlane('z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0); // px
        buildPlane('z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1); // nx
        buildPlane('x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py
        buildPlane('x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3); // ny
        buildPlane('x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4); // pz
        buildPlane('x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5); // nz

        // Store vertex coordinates, vertex normal vector, vertex texture coordinates and vertex index
        this.setIndex(indices);
        this.setAttribute('position', new Float32BufferAttribute(vertices, 3));
        this.setAttribute('normal', new Float32BufferAttribute(normals, 3));
        this.setAttribute('uv', new Float32BufferAttribute(uvs, 2));

        function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { }
    }

    // ...
}

Material class

Material

Base class of various materials.

Source code overview:

let materialId = 0;

class Material extends EventDispatcher {
    constructor() {
        super();

        Object.defineProperty(this, 'id', { value: materialId++ });

        this.type = 'Material';

        this.vertexColors = false; // Use vertex color
        this.format = RGBAFormat;
        this.opacity = 1; // transparency
        this.transparent = false; // Do you want to use transparency

        // blend
        this.blending = NormalBlending;
        this.blendSrc = SrcAlphaFactor;
        this.blendDst = OneMinusSrcAlphaFactor;

        // Depth detection
        this.depthFunc = LessEqualDepth;
        this.depthTest = true;

        // polygon offset 
        this.polygonOffset = false;
        this.polygonOffsetFactor = 0;
        this.polygonOffsetUnits = 0;

        // ...
    }

    // ...
}

MeshBasicMaterial

Base mesh material.

Source code overview:

/**
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *  map: new THREE.Texture( <Image> ),
 *
 *  lightMap: new THREE.Texture( <Image> ),
 *  lightMapIntensity: <float>
 *
 *  aoMap: new THREE.Texture( <Image> ),
 *  aoMapIntensity: <float>
 *
 *  specularMap: new THREE.Texture( <Image> ),
 *
 *  alphaMap: new THREE.Texture( <Image> ),
 *
 *  envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
 *  combine: THREE.Multiply,
 *  reflectivity: <float>,
 *  refractionRatio: <float>,
 *
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>,
 * }
 */
class MeshBasicMaterial extends Material {
    constructor(parameters) {
        super();

        this.type = 'MeshBasicMaterial';

        // ...
    }

    // ...
}

Renderer

WebGLRenderer

Source code overview:

function WebGLRenderer(parameters = {}) {
    // Canvas dom and canvas size
    this.domElement = _canvas;
    let _width = _canvas.width;
    let _height = _canvas.height;

    _canvas.addEventListener('webglcontextlost', onContextLost, false);
    _canvas.addEventListener('webglcontextrestored', onContextRestore, false);

    // The internal code of render function is very complex. You can see it later if necessary
    this.render = function (scene, camera) {
        // ...
    }

    // ...
}

Topics: Three.js