three.js 筆記七 Matrix

一、行主序、列主序

概念參考行主序 列主序

以線性代數(shù)中描述的矩陣為標(biāo)準(zhǔn),行主序就是依次按行存儲(chǔ),而列主序就是依次按列存儲(chǔ)。在threeJS中:

var A = new THREE.Matrix4();
A.set(1, 2, 3, 4,
    5, 6, 7, 8,
    9, 10, 11, 12,
    13, 14, 15, 16);
console.log(A);

var B = new THREE.Matrix4();
B.set(16, 15, 14, 13,
    12, 11, 10, 9,
    8, 7, 6, 5,
    4, 3, 2, 1);
console.log(B);

var C = new THREE.Matrix4();
C.multiplyMatrices (A, B);    
console.log(C);

其運(yùn)行結(jié)果為:

image.png

在網(wǎng)上找一個(gè)在線矩陣計(jì)算器,比如http://www.yunsuan.info/matrixcomputations/solvematrixmultiplication.html
相對(duì)應(yīng)的計(jì)算結(jié)果如下:
image.png

因此可以認(rèn)為,threejs矩陣內(nèi)部?jī)?chǔ)存形式為列主序,表達(dá)和描述的仍然是線性代數(shù)中行主序,set()函數(shù)就是以行主序接受矩陣參數(shù)的。

二、如何根據(jù)變換設(shè)計(jì)自己的矩陣

概念性的東西,可以參考
線性代數(shù)筆記三 線性變換和矩陣乘法
圖形學(xué)筆記一 仿射變換和齊次坐標(biāo)

1.向量或點(diǎn)的縮放平移等操作

這部分比較好處理,例子可以參考
three.js 之 Matrix

2.坐標(biāo)系的轉(zhuǎn)化

馮樂樂講MVP的例子也很好,可以參考
UnityShader精要筆記二 數(shù)學(xué)基礎(chǔ)

核心思路就是以世界坐標(biāo)為中轉(zhuǎn),應(yīng)用坐標(biāo)的變換等價(jià)于基變換。

比如模型坐標(biāo)系轉(zhuǎn)世界坐標(biāo)系,就是模型空間任意一點(diǎn)計(jì)算出其在世界坐標(biāo)系的位置。即模型每個(gè)點(diǎn)動(dòng)了,整個(gè)模型也動(dòng)了。

而世界坐標(biāo)系轉(zhuǎn)觀察坐標(biāo)系,則是先用觀察坐標(biāo)系轉(zhuǎn)世界坐標(biāo)系之后求逆,這樣快速運(yùn)算。所以說,世界坐標(biāo)系是用來中轉(zhuǎn)的,世界中心點(diǎn)不會(huì)動(dòng)。

三、THREEJS封裝的矩陣API
1.平移
var vector = new THREE.Vector3(20, 20, 0);
var matrix = new THREE.Matrix4();
matrix.makeTranslation(10, 40, 0);
vector.applyMatrix4(matrix);
2.旋轉(zhuǎn)
matrix.makeRotationX(angle);
matrix.makeRotationY(angle);
matrix.makeRotationZ(angle);
matrix.makeRotationAxis(axis, angle);
matrix.makeRotationFromEuler(euler);
matrix.makeRotationFromQuaternion(quaternion);

前三個(gè)方法分別代表的是繞X、Y、Z三個(gè)軸旋轉(zhuǎn),無需贅述。
第四個(gè)方法是前三個(gè)方法的整合版,第一個(gè)參數(shù)表示的是代表xyz的THREE.Vector3,第二個(gè)參數(shù)是旋轉(zhuǎn)的弧度。下面兩行代碼是等價(jià)的:

matrix.makeRotationX(Math.PI);
matrix.makeRotationAxis(new THREE.Vector3(1, 0, 0), Math.PI);

第五個(gè)方法表示圍繞x、y和z軸的旋轉(zhuǎn),這是表示旋轉(zhuǎn)最常用的方式;第六個(gè)方法是一種基于軸和角度表示旋轉(zhuǎn)的替代方法。

關(guān)于旋轉(zhuǎn),可以參考
Cocos 3.x 四元數(shù) rotateAroundLocal
Three.js歐拉對(duì)象Euler和四元數(shù)Quaternion

構(gòu)造函數(shù):Euler(x,y,z,order)
參數(shù)xyz分別表示繞xyz軸旋轉(zhuǎn)的角度值,角度單位是弧度。參數(shù)order表示旋轉(zhuǎn)順序,默認(rèn)值XYZ,也可以設(shè)置為YXZ、YZX等值

// 創(chuàng)建一個(gè)歐拉對(duì)象,表示繞著xyz軸分別旋轉(zhuǎn)45度,0度,90度
var Euler = new THREE.Euler( Math.PI/4,0, Math.PI/2);

四元數(shù)的方法.setFromAxisAngle(axis, angle)通過旋轉(zhuǎn)軸axis和旋轉(zhuǎn)角度angle設(shè)置四元數(shù)數(shù)據(jù),也就是x、y、z和w四個(gè)分量。

var quaternion = new THREE.Quaternion();
// 旋轉(zhuǎn)軸new THREE.Vector3(0,1,0)
// 旋轉(zhuǎn)角度Math.PI/2
quaternion.setFromAxisAngle(new THREE.Vector3(0,1,0),Math.PI/2)
console.log('查看四元數(shù)結(jié)構(gòu)',quaternion);

四元數(shù)乘法.multiply()
對(duì)象的一個(gè)旋轉(zhuǎn)可以用一個(gè)四元數(shù)表示,兩次連續(xù)旋轉(zhuǎn)可以理解為兩次旋轉(zhuǎn)對(duì)應(yīng)的四元數(shù)對(duì)象進(jìn)行乘法運(yùn)算。

// 四元數(shù)q1、q2分別表示一個(gè)旋轉(zhuǎn),兩個(gè)四元數(shù)進(jìn)行乘法運(yùn)算,相乘結(jié)果保存在q2中
// 在q1表示的旋轉(zhuǎn)基礎(chǔ)在進(jìn)行q2表示的旋轉(zhuǎn)操作
q1.quaternion.multiply( q2 );

歐拉、四元數(shù)和矩陣轉(zhuǎn)化
歐拉對(duì)象、四元數(shù)對(duì)象和旋轉(zhuǎn)矩陣可以相關(guān)轉(zhuǎn)化,都可以表示旋轉(zhuǎn)變換。

//通過矩陣對(duì)象Matrix4的.makeRotationFromQuaternion(q)方法可以把四元數(shù)轉(zhuǎn)化對(duì)應(yīng)的矩陣對(duì)象。
Matrix4.makeRotationFromQuaternion(q)

//通過歐拉對(duì)象設(shè)置四元數(shù)對(duì)象
quaternion.setFromEuler(Euler)

//四元數(shù)轉(zhuǎn)化為歐拉對(duì)象
Euler.setFromQuaternion(quaternion)

Object3D對(duì)象角度屬性.rotation的值是歐拉對(duì)象Euler,四元數(shù)屬性.quaternion的值是四元數(shù)對(duì)象Quaternion。

執(zhí)行Object3D對(duì)象旋轉(zhuǎn)方法,會(huì)同時(shí)改變對(duì)象的角度屬性和四元數(shù)屬性。四元數(shù)屬性和位置.position、縮放屬性.scale一樣會(huì)轉(zhuǎn)化為對(duì)象的本地矩陣屬性.matrix,本地矩陣屬性值包含了旋轉(zhuǎn)矩陣、縮放矩陣、平移矩陣。

Object3D對(duì)象角度屬性.rotation和四元數(shù)屬性.quaternion是相互關(guān)聯(lián)的一個(gè)改變會(huì)同時(shí)改變另一個(gè)。

// 一個(gè)網(wǎng)格模型對(duì)象,基類是Object3D
var mesh = new THREE.Mesh()
// 繞z軸旋轉(zhuǎn)
mesh.rotateZ(Math.PI)

console.log('查看角度屬性rotation',mesh.rotation);
console.log('查看四元數(shù)屬性quaternion',mesh.quaternion);
3.compose

參考Three.js 克隆其他模型的矩陣 Matrix4

image.png

//使用make系列的方法操作
Object3D.applyMatrix(new THREE.Matrix4().makeScale(2,1,1));
Object3D.applyMatrix(new THREE.Matrix4().makeTranslation(0,4,0));
Object3D.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI/6));
//使用compose方法操作
var matrix = new THREE.Matrix4();
var trans = new THREE.Vector3(0,4,0);
var rotat = new THREE.Quaternion().setFromEuler(new THREE.Euler(0,0,Math.PI/6));
var scale = new THREE.Vector3(2,1,1);
Object3D.applyMatrix4(matrix.compose(trans, rotat, scale)); //效果同上
image.png

就是compose的逆過程。隨便舉個(gè)例子。

var matrix = new THREE.Matrix4().set(1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7);
var trans = new THREE.Vector3();
var rotat = new THREE.Quaternion();
var scale = new THREE.Vector3();
matrix.decompose(trans, rotat, scale);

//返回Vector3 {x: 4, y: 5, z: 6} 因?yàn)槭请S便寫的,所以只有平移變量不需計(jì)算就可以看出來的
console.log(trans); 

//返回Quaternion {_x: 0.05565363763555474, _y: -0.11863820054057297
//, _z: 0.051265314875937947, _w: 0.7955271896092125}
console.log(rotat); 

//返回Vector3 {x: 3.7416573867739413, y: 5.385164807134504, z: 7.0710678118654755}
console.log(scale); 

如何通過矩陣設(shè)置Object3D對(duì)象位置呢,參考108 THREE.JS 使用矩陣對(duì)3D對(duì)象進(jìn)行位置設(shè)置

//最后先將模型移動(dòng)到中心位置
var inverseM = new THREE.Matrix4();
inverseM.getInverse(centerM);
matrix.multiply(inverseM);

//將矩陣賦值給模型
cube.matrix = matrix;

//使用矩陣更新模型的信息
cube.matrix.decompose(cube.position, cube.quaternion, cube.scale);

還有個(gè)注意點(diǎn),就是threejs的decompose是X軸方向的負(fù)縮放,有可能與其它3D庫的matrix.decompose不同(比如是Z軸方向的負(fù)縮放)


image.png

測(cè)試的matrix數(shù)據(jù)如下:
rawData
(16) [5.053215498074303e-16, -1, 6.123233995736772e-17, 0, -1, -5.053215498074303e-16, -1.2246467991473532e-16, 0, -1.2246467991473535e-16, 6.123233995736766e-17, 1, 0, 1282.7820737078252, 3307.4240662137445, 3.128811373451107e-13, 1]

企業(yè)微信截圖_17144707166307.png

企業(yè)微信截圖_17144707508493.png
4.相乘

之前用過的matrix.multiplyMatrices(matrixA, matrixB),表示 將矩陣設(shè)置為matrixA * matrixB的結(jié)果。

threejs矩陣還有前乘和后乘的區(qū)別,也很容易混淆。

在threeJS中矩陣的后乘方法為multiply():

var A = new THREE.Matrix4();
A.set(1, 2, 3, 4,
    5, 6, 7, 8,
    9, 10, 11, 12,
    13, 14, 15, 16);

var B = new THREE.Matrix4();
B.set(16, 15, 14, 13,
    12, 11, 10, 9,
    8, 7, 6, 5,
    4, 3, 2, 1);

A.multiply(B);
console.log(A);
console.log(B);

其運(yùn)行結(jié)果為:


image.png

表明A.multiply(B)不會(huì)改變B的值,會(huì)改變A的值,相當(dāng)于A=A*B,即后乘方法multiply()的結(jié)果就是把傳入的參數(shù)放自己后面去乘。
反過來,使用前乘方法A.premultiply(B);,結(jié)果就是B?A

5.逆矩陣
let m4 = new THREE.Matrix4();
m4.elements = [10, 8, 3, 0, 15, 7, 2, 0, 10, 6, 1, 0, 0, 0, 0, 1];
        
let m3 = new THREE.Matrix4();
//執(zhí)行這行,會(huì)將m4的逆矩陣設(shè)置給m3
m3.getInverse(m4);
6.例子

image.png

參考Three.js中的矩陣,做出如圖的旋轉(zhuǎn)效果:

var box_geometry = new THREE.BoxGeometry();
var sphere_geometry = new THREE.SphereGeometry(0.5, 32, 32);
var cylinder_geometry = new THREE.CylinderGeometry(0.1, 0.1, 0.5);

var material = new THREE.MeshLambertMaterial({color: new THREE.Color(0.9, 0.55, 0.4)});

var box = new THREE.Mesh(box_geometry, material);
var sphere = new THREE.Mesh(sphere_geometry, material);
var cylinder = new THREE.Mesh(cylinder_geometry, material);

scene.add(box);
scene.add(sphere);
scene.add(cylinder);

box.matrixAutoUpdate = false;
sphere.matrixAutoUpdate = false;
cylinder.matrixAutoUpdate = false;

var sphere_matrix = new THREE.Matrix4().makeTranslation(0.0, 1.0, 0.0); 
sphere_matrix.multiply(new THREE.Matrix4().makeRotationZ(-Math.PI * 0.25));
sphere.applyMatrix(sphere_matrix);

var cylinder_matrix = sphere_matrix.clone(); 
cylinder_matrix.multiply(new THREE.Matrix4().makeTranslation(0.0, 0.75, 0.0)); 

cylinder.applyMatrix(cylinder_matrix);


注意這個(gè)例子中只給了部分代碼,由于使用的是MeshLambertMaterial,需要添加光照才能看到幾何體,當(dāng)然也可以換其它material:

// 環(huán)境光
const ambientLight = new THREE.AmbientLight(0xffffff, 1); // 創(chuàng)建環(huán)境光
this.scene.add(ambientLight); // 將環(huán)境光添加到場(chǎng)景

如果因?yàn)閠hreejs版本問題,applyMatrix報(bào)undefined,改成applyMatrix4即可。

也可以參考這個(gè)例子,確認(rèn)一下multiply的用法,上面有提到結(jié)論:

表明A.multiply(B)不會(huì)改變B的值,會(huì)改變A的值,相當(dāng)于A=A*B

var sphere_matrix = new THREE.Matrix4().makeTranslation(0.0, 1.0, 0.0); 
sphere.applyMatrix(sphere_matrix);
var cylinder_matrix = sphere_matrix.clone(); 
cylinder_matrix.multiply(new THREE.Matrix4().makeTranslation(0.0, 0.75, 0.0)); 
cylinder.applyMatrix(cylinder_matrix);

這段代碼和之前的功能是一樣的:

var box = new THREE.Mesh(box_geometry, material);
var sphere = new THREE.Mesh(sphere_geometry, material);
sphere.position.y += 1;
var cylinder = new THREE.Mesh(cylinder_geometry, material);
cylinder.position.y += 1.75;
scene.add(box);
scene.add(sphere);
scene.add(cylinder);

可以看出,cylinder_matrix使用multiply,在之前的平移矩陣上,再移動(dòng)了0.75

但是,如果矩陣中還有其他值,使用乘法,都會(huì)改變:

        let ttt = new THREE.Matrix4().set(1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7);
        ttt.multiply(new THREE.Matrix4().makeTranslation(10,20,30));
        console.log("ttt:",ttt.elements);

        let ttt2 = new THREE.Matrix4().set(1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7);
        ttt2.premultiply(new THREE.Matrix4().makeTranslation(10,20,30));
        console.log("ttt2:",ttt2.elements);

結(jié)果:

ttt: (16) [1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, 144, 205, 266, 327]
ttt2: (16) [41, 82, 123, 4, 52, 103, 154, 5, 63, 124, 185, 6, 74, 145, 216, 7]
spher: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1] cylinder: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1.75, 0, 1]
7.Object3D.matrix和matrixWorld

仍然以UnityShader精要筆記二 數(shù)學(xué)基礎(chǔ)中MVP的例子:

就是有個(gè)奶牛叫妞妞,她有自己的坐標(biāo)空間即模型空間,在這個(gè)空間里,她的鼻子坐標(biāo)是(0,2,4),最后如何顯示在屏幕上呢?首先,轉(zhuǎn)化為齊次坐標(biāo)(0,2,4,1)。頂點(diǎn)變換的第一步就是將頂點(diǎn)坐標(biāo)從模型空間變換到世界空間,這個(gè)變換通常叫做模型變換(model transform)。根據(jù)Transform的信息,妞妞進(jìn)行了(2,2,2)的縮放,(0,150,0)的旋轉(zhuǎn)以及(5,0,25)的平移。根據(jù)之前的知識(shí),要先縮放再旋轉(zhuǎn)再平移:

image.png

我們使用代碼來驗(yàn)證一下:

var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
cube.scale.set(2, 2, 2);
cube.rotateY(150 * Math.PI / 180);
cube.position.set(5, 0, 25);
var vec = new THREE.Vector4(0, 2, 4, 1);
vec.applyMatrix4(cube.matrix);
console.log("vec:", vec);

現(xiàn)在打印出來的是0,2,4,1 這是因?yàn)閙atrix并沒有立即生效,可以手動(dòng)調(diào)用cube.updateMatrix(),關(guān)于更新的問題后面再說,現(xiàn)在先換一個(gè)打印方式:

function animate() {
    requestAnimationFrame(animate);
    // cube.rotation.x += 0.01;
    // cube.rotation.y += 0.01;
    renderer.render(scene, camera);
    var vec = new THREE.Vector4(0, 2, 4, 1);
    vec.applyMatrix4(cube.matrix);
    console.log("vec:", vec);
}
//animate();

打印結(jié)果與例子中計(jì)算結(jié)果一致:vec: Vector4 {x: 9, y: 4, z: 18.07179676972449, w: 1}

這也說明,cube.matrix是由模型坐標(biāo)系轉(zhuǎn)向其父容器世界坐標(biāo)系的?,F(xiàn)在繼續(xù)做測(cè)試,把cube再添加一個(gè)父容器:

var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
cube.scale.set(2, 2, 2);
cube.rotateY(150 * Math.PI / 180);
cube.position.set(5, 0, 25);
var vec = new THREE.Vector4(0, 2, 4, 1);
vec.applyMatrix4(cube.matrix);

var cubeParent = new THREE.Object3D();
cubeParent.position.set(3, 0, 0);
cubeParent.add(cube);
scene.add(cubeParent);

然后打印的地方,把cube.matrix和cube.matrixWorld都打?。?/p>

function animate() {
    requestAnimationFrame(animate);
    // cube.rotation.x += 0.01;
    // cube.rotation.y += 0.01;
    renderer.render(scene, camera);
    var vec = new THREE.Vector4(0, 2, 4, 1);
    vec.applyMatrix4(cube.matrix);
    console.log("vec:", vec);
    console.log("matrix:", cube.matrix);
    console.log("matrixWorld:", cube.matrixWorld);
}
image.png

image.png

顯然,能看出cube.matrixWorld是把嵌套的父容器也考慮進(jìn)去,一步到位,直接轉(zhuǎn)到世界坐標(biāo)系。

cube.modelViewMatrix
表示對(duì)象相對(duì)于相機(jī)坐標(biāo)系的變換。也就是matrixWorld左乘相機(jī)的matrixWorldInverse。
但是,我打印一下,發(fā)現(xiàn)這個(gè)值不對(duì):

image.png

那沒辦法,我們自己用矩陣乘法計(jì)算:

camera.rotateX(30 * Math.PI / 180);
camera.position.set(0, 10, -10);

function animate() {
    requestAnimationFrame(animate);
    // cube.rotation.x += 0.01;
    // cube.rotation.y += 0.01;
    renderer.render(scene, camera);
    var vec = new THREE.Vector4(0, 2, 4, 1);
    vec.applyMatrix4(cube.matrix);
    console.log("vec:", vec);
    console.log("matrix:", cube.matrix);
    console.log("matrixWorld:", cube.matrixWorld);

    var vec2 = new THREE.Vector4(0, 2, 4, 1);
    let m = cube.matrixWorld.clone();
    m.premultiply(camera.matrixWorldInverse);
    vec2.applyMatrix4(m);
    console.log("vec2:", vec2);

回到我們的農(nóng)場(chǎng)游戲?,F(xiàn)在我們需要把妞妞的鼻子從世界空間變換到觀察空間中。為此我們需要知道世界坐標(biāo)系下攝像機(jī)的變換信息。這同樣可以通過攝像機(jī)面板中的Transform組件得到:(1,1,1)的縮放,(30,0,0)的旋轉(zhuǎn),(0,10,-10)的平移。

image.png

image.png

可以看到結(jié)果與例子中的Z值是相反的,這是因?yàn)閡nity用的左手坐標(biāo)系,而threejs是右手坐標(biāo)系。

注,這里用的是premultiply,可以參考之前的結(jié)論:

表明A.multiply(B)不會(huì)改變B的值,會(huì)改變A的值,相當(dāng)于A=A*B

8.camera相關(guān)的matrix

攝像機(jī)Cameras 有兩個(gè)額外的四維矩陣:

  • Camera.matrixWorldInverse: 視圖矩陣 - 攝像機(jī)世界坐標(biāo)變換的逆矩陣。
  • Camera.projectionMatrix: 投影矩陣 - 表示將場(chǎng)景中的信息投影到裁剪空間。
9.makeScale會(huì)清理其它數(shù)據(jù)
let ttt = new THREE.Matrix4().set(1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7);
ttt.makeScale(2,2,2);
console.log("ttt:",ttt.elements);

let ttt2 = new THREE.Matrix4().set(1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7);
ttt2.makeTranslation(1,2,3);
console.log("ttt2:",ttt2.elements);

let ttt3 = new THREE.Matrix4().set(1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7);
ttt3.compose(new THREE.Vector3(2,2,2),new THREE.Quaternion(),new THREE.Vector3(1,2,3));
console.log("ttt3:",ttt3.elements);

結(jié)果如下:

ttt: (16) [2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1]

ttt2: (16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1]

ttt3: (16) [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 2, 2, 2, 1]

也就是說,如果不想清理之前的數(shù)據(jù),可以使用compose進(jìn)行一次性轉(zhuǎn)換。

10.更多的API

參考
three.js 數(shù)學(xué)方法之Matrix4

四、THREEJS來更新對(duì)象的變換

參考
three.js 之 Matrix
學(xué)習(xí)ThreeJS 04 更新機(jī)制

1.更改對(duì)象的位置,四元數(shù),和伸縮屬性,three.js 會(huì)根據(jù)這些屬性重新計(jì)算對(duì)象的矩陣:
object.position.copy(start_position);
object.quaternion.copy(quaternion);

默認(rèn)情況下,matrixAutoUpdate 屬性是設(shè)置為 true 的,矩陣會(huì)自動(dòng)重新計(jì)算(如果它們已添加到場(chǎng)景中,或者是已添加到場(chǎng)景中的另一個(gè)對(duì)象的子節(jié)點(diǎn))。

var object1 = new THREE.Object3D();
var object2 = new THREE.Object3D();
object1.add( object2 );

//object1 和 object2 會(huì)自動(dòng)更新它們的矩陣
scene.add( object1 ); 

如果對(duì)象是靜態(tài)的,或者你希望自己手動(dòng)控制什么時(shí)候重新計(jì)算,可以通過將屬性設(shè)置為 false 來獲取更好的性能。

object.matrixAutoUpdate = false

同時(shí)在改變?nèi)魏螌傩灾?,手?dòng)更新矩陣:

object.updateMatrix();
2.直接修改對(duì)象的矩陣
object.matrix.setRotationFromQuaternion(quaternion);
object.matrix.setPosition(start_position);
object.matrixAutoUpdate = false;

注意在這種情況下 matrixAutoUpdate 必須設(shè)置成 false。并且你要確定不要調(diào)用 updateMatrix 方法。調(diào)用 updateMatrix 會(huì)阻斷對(duì)矩陣的手動(dòng)更改,會(huì)根據(jù)位置、伸縮等屬性重新計(jì)算矩陣。

3.matrixWorldNeedsUpdate

參考https://sogrey.top/Three.js-start/cores/#Object3D
matrixWorldNeedsUpdate : Boolean
當(dāng)這個(gè)屬性設(shè)置了之后,它將計(jì)算在那一幀中的matrixWorld,并將這個(gè)值重置為false。默認(rèn)值為false。

五、窗口 resize事件更新

參考Three.js自適應(yīng)窗口變化渲染

  • 發(fā)生場(chǎng)景:當(dāng)窗口大小發(fā)生變化時(shí),會(huì)出現(xiàn)局部空白區(qū)域。
  • 解決方法:重新獲取瀏覽器窗口新的寬高尺寸,然后通過新的寬高尺寸更新相機(jī)Camera和渲染器WebGLRenderer的參數(shù)。
  • 要注意一下,Three.js自適應(yīng)渲染不一定就是窗口變化,本質(zhì)上還是你要渲染的區(qū)域?qū)捀叱叽缱兓?;更進(jìn)一步變化是視圖矩陣.matrixWorldInverse和投影矩陣.projectionMatrix的變化。
// onresize 事件會(huì)在窗口被調(diào)整大小時(shí)發(fā)生
window.οnresize=function(){
  // 重置渲染器輸出畫布canvas尺寸
  renderer.setSize(window.innerWidth,window.innerHeight);
  // 全屏情況下:設(shè)置觀察范圍長(zhǎng)寬比aspect為窗口寬高比
  camera.aspect = window.innerWidth/window.innerHeight;
  // 渲染器執(zhí)行render方法的時(shí)候會(huì)讀取相機(jī)對(duì)象的投影矩陣屬性projectionMatrix
  // 但是不會(huì)每渲染一幀,就通過相機(jī)的屬性計(jì)算投影矩陣(節(jié)約計(jì)算資源)
  // 如果相機(jī)的一些屬性發(fā)生了變化,需要執(zhí)行updateProjectionMatrix ()方法更新相機(jī)的投影矩陣
  camera.updateProjectionMatrix ();
};

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容