在项目上,我们会通过设置 Mesh 上的rotation 属性来实现模型旋转的控制,那其中threejs 是怎么实现的呢?
调用 mesh.rotation 属性实现旋转
通过 Object3D 源码可以知道, rotation 其实是代理了 Euler 实例,当我们设置object.rotation.x , 其实调用的是 euler.x 设置。些时在 Object3D 中注册了一个变更回调,在更新了x值后会执行回调,在回调中会更新 Object3D 上的四元数计算旋转值。
image.png
在回调中他没有立马去更新 模型的 matrixWorld。当下次渲染函数执行的时候才会触发更新,更新流程如下
- 当执行renderer.render 的时候,会调用scene.updateMatrixWorld 方法,通过updatematrixWorld 方法会调用 updateMatrix。 在updatematrixWorld 方法中会递归调用子节点的此方法。从而更新模型 的matrixWorld.
updateMatrix() {
// 实现与四元数的结合
this.matrix.compose( this.position, this.quaternion, this.scale );
this.matrixWorldNeedsUpdate = true;
}
updateMatrixWorld( force ) {
if ( this.matrixAutoUpdate ) this.updateMatrix();
if ( this.matrixWorldNeedsUpdate || force ) {
if ( this.parent === null ) {
this.matrixWorld.copy( this.matrix );
} else {
// 更新 matrixWorld
this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
}
this.matrixWorldNeedsUpdate = false;
force = true;
}
// update children
const children = this.children;
for ( let i = 0, l = children.length; i < l; i ++ ) {
const child = children[ i ];
if ( child.matrixWorldAutoUpdate === true || force === true ) {
child.updateMatrixWorld( force );
}
}
}
在真实渲染模型 的时候,会在renderer.renderObject 方法中计算模型 的modelViewMatrix,这个矩阵就是着色器用来计算模型旋转后位置 的矩阵。从而实现旋转
function renderObject( object, scene, camera, geometry, material, group ) {
object.onBeforeRender( _this, scene, camera, geometry, material, group );
// 计算 modelViewMatrix
object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
material.onBeforeRender( _this, scene, camera, geometry, object, group );
if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) {
material.side = BackSide;
material.needsUpdate = true;
_this.renderBufferDirect( camera, scene, geometry, material, object, group );
material.side = FrontSide;
material.needsUpdate = true;
_this.renderBufferDirect( camera, scene, geometry, material, object, group );
material.side = DoubleSide;
} else {
_this.renderBufferDirect( camera, scene, geometry, material, object, group );
}
object.onAfterRender( _this, scene, camera, geometry, material, group );
}