下面是本文例子的運(yùn)行截圖为黎,可以前往我的博客查看代碼演示冯事。
上一篇文章中說到了透視和正交兩種投影矩陣,文末提到了三個(gè)基本矩陣MVP。本文就以介紹MVP為開頭信殊,然后再詳細(xì)講解攝像機(jī)的概念挽绩。
MVP表示的是模型變換矩陣(Model),觀察矩陣(View)堰塌,投影矩陣(Projection)赵刑。投影矩陣介紹過了。模型矩陣針對的是單個(gè)3D模型场刑,渲染每一個(gè)3D模型前般此,需要將各自的模型矩陣傳遞給Vertex Shader。觀察矩陣針對的是場景中的所有物體牵现,當(dāng)觀察矩陣改變時(shí)铐懊,所有頂點(diǎn)的位置都會(huì)受到影響,就好像你移動(dòng)現(xiàn)實(shí)世界的攝像機(jī)瞎疼,拍攝到的場景就會(huì)變化一樣科乎。所以觀察矩陣可以理解為WebGL 3D世界中的攝像機(jī)。我們有了攝像機(jī)這個(gè)變換矩陣之后贼急,就可以很方便的在3D世界中游覽茅茂,就像第一人稱視角游戲中一樣。
大概了解MVP之后太抓,我們開始使用代碼實(shí)現(xiàn)它們空闲。首先要修改一下Vertex Shader。
attribute vec4 position;
varying vec4 fragColor;
uniform float elapsedTime;
uniform mat4 projectionMatrix;
uniform mat4 cameraMatrix;
uniform mat4 modelMatrix;
void main() {
fragColor = position * 0.5 + 0.5;
gl_Position = projectionMatrix * cameraMatrix * modelMatrix * position;
}
我把之前的uniform transform
換成了三個(gè)變換矩陣projectionMatrix
,cameraMatrix
,modelMatrix
,它們分別是投影矩陣走敌,觀察矩陣碴倾,模型矩陣。將它們相乘projectionMatrix * cameraMatrix * modelMatrix
,結(jié)果乘以position
賦值給gl_Position
影斑。注意相乘的順序给赞,這個(gè)順序的結(jié)果是先進(jìn)行模型矩陣變換,再是觀察矩陣矫户,最后是投影矩陣變換片迅。這樣Vertex Shader中的MVP就實(shí)現(xiàn)完了,很簡單是不是皆辽。
回到JS代碼柑蛇,我將之前的屬性transform換成了4個(gè)變換矩陣,分別是兩個(gè)M和VP驱闷。本文的例子將繪制兩個(gè)矩形耻台,所以我為它們分別定義了模型矩陣modelMatrix1
和modelMatrix2
。
var perspectiveProjectionMatrix = null;
var cameraMatrix = null;
var modelMatrix1 = null;
var modelMatrix2 = null;
接下來初始化這些屬性空另。
function setupMatrix() {
perspectiveProjectionMatrix = mat4.create();
mat4.perspective(perspectiveProjectionMatrix, 90 / 180.0 * Math.PI, canvas.width / canvas.height, 0.1, 1000);
cameraMatrix = mat4.create();
mat4.lookAt(cameraMatrix, vec3.fromValues(0,0,2), vec3.fromValues(0,0,0), vec3.fromValues(0,1,0));
modelMatrix1 = mat4.create();
modelMatrix2 = mat4.create();
}
投影矩陣使用了透視投影進(jìn)行初始化盆耽。兩個(gè)模型矩陣初始化為單位矩陣。本文的主角觀察矩陣初始化為攝像機(jī)在 0扼菠,0摄杂,2 坐標(biāo),看向 0循榆,0析恢,0點(diǎn),向上朝向0秧饮,1映挂,0。mat4.lookAt
提供了快捷創(chuàng)建觀察矩陣的方法盗尸,需要傳遞3個(gè)類型為vec3
的參數(shù)柑船,攝像機(jī)的位置(eyeX,eyeY振劳,eyeZ)
椎组,攝像機(jī)看向的點(diǎn)(centerX油狂,centerY历恐,centerZ)
,攝像機(jī)向上的朝向(upX, upY, upZ)
专筷。改變這幾個(gè)參數(shù)就能控制攝像機(jī)在3D世界中通過不同角度拍攝物體弱贼。
我把上一篇的剖面示意圖做了一下修改。
圖中的lookAt就是center磷蛹。
我們可以這么理解觀察矩陣吮旅。在觀察矩陣的作用下,透視矩陣的原點(diǎn)變成了攝像機(jī)的位置eye。up決定了攝像機(jī)圍繞eye和lookAt形成的軸(本例中就是Z軸)的旋轉(zhuǎn)角度庇勃,讀者可以修改本例的中的up值看看效果檬嘀。lookAt決定了攝像機(jī)能看到的區(qū)域,可以看做是控制攝像機(jī)在Y軸和X軸上的旋轉(zhuǎn)角度责嚷。
在第一人稱的游戲中鸳兽,只要控制lookAt的位置就可以實(shí)現(xiàn)360度查看周邊景物的效果,后面介紹到渲染3D場景的時(shí)候會(huì)深入講解罕拂。
初始化完后在onWebGLRender
中為這些矩陣賦新的值揍异。
// 保證canvas尺寸改變時(shí),同步投影矩陣的值爆班,你也可以在resize里重新計(jì)算衷掷,那樣會(huì)更好。
mat4.perspective(perspectiveProjectionMatrix, 90 / 180.0 * Math.PI, canvas.width / canvas.height, 0.1, 1000);
var varyingFactor = (Math.sin(elapsedTime / 1000) + 1) / 2.0; // 0 ~ 1
mat4.lookAt(cameraMatrix, vec3.fromValues(0, 0, 2 * (varyingFactor + 1)), vec3.fromValues(0, 0, 0), vec3.fromValues(0, 1, 0));
// 設(shè)置第一個(gè)model matrix
var rotateMatrix = mat4.create();
var translateMatrix = mat4.create();
mat4.rotate(rotateMatrix, rotateMatrix, varyingFactor * Math.PI * 2, vec3.fromValues(0, 1, 0));
mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(-0.7, 0, 0));
mat4.multiply(modelMatrix1, translateMatrix, rotateMatrix);
// 設(shè)置第二個(gè)model matrix
rotateMatrix = mat4.create();
translateMatrix = mat4.create();
mat4.rotate(rotateMatrix, rotateMatrix, varyingFactor * Math.PI * 2, vec3.fromValues(0, 0, 1));
mat4.translate(translateMatrix, translateMatrix, vec3.fromValues(0.7, 0, 0));
mat4.multiply(modelMatrix2, translateMatrix, rotateMatrix);
var varyingFactor = (Math.sin(elapsedTime / 1000) + 1) / 2.0;
的值從0到1柿菩。
攝像機(jī)的Z軸坐標(biāo)為2 * (varyingFactor + 1)
戚嗅,從2到4。
第一個(gè)矩形向左偏移0.7枢舶,繞Y軸旋轉(zhuǎn)varyingFactor * M_PI * 2
渡处,從0到360度。
第二個(gè)矩形向右偏移0.7祟辟,繞Z軸旋轉(zhuǎn)varyingFactor * M_PI * 2
医瘫,從0到360度。
最后給uniform賦值旧困。
// 設(shè)置投影和觀察矩陣
var projectionMatrixUniformLoc = gl.getUniformLocation(program, 'projectionMatrix');
gl.uniformMatrix4fv(projectionMatrixUniformLoc, false, perspectiveProjectionMatrix);
var cameraMatrixUniformLoc = gl.getUniformLocation(program, 'cameraMatrix');
gl.uniformMatrix4fv(cameraMatrixUniformLoc, false, cameraMatrix);
// 使用不同的model matrix繪制兩次物體
var modelMatrixUniformLoc = gl.getUniformLocation(program, 'modelMatrix');
gl.uniformMatrix4fv(modelMatrixUniformLoc, false, modelMatrix1);
gl.drawArrays(gl.TRIANGLES, 0, 6);
modelMatrixUniformLoc = gl.getUniformLocation(program, 'modelMatrix');
gl.uniformMatrix4fv(modelMatrixUniformLoc, false, modelMatrix2);
gl.drawArrays(gl.TRIANGLES, 0, 6);
先給uniform projectionMatrix
和uniform cameraMatrix
賦值醇份。每個(gè)矩形繪制之前,再將各自的modelMatrix賦值給uniform modelMatrix
吼具,就像開頭說的那樣僚纷,每個(gè)3D模型有自己的模型變換。
本篇主要介紹了攝像機(jī)(觀察矩陣)拗盒,三大基本矩陣MVP的概念怖竭。下一篇小試牛刀,開始渲染真正的3D物體-正方體陡蝇。