WebGL從2012年開(kāi)始接觸,后面因?yàn)殚_(kāi)始專注前端其他方面的事情,慢慢地就把它給遺忘。最近前端開(kāi)始又流行起繪畫制作,游戲、VR等等又開(kāi)始引起前端人們的注意。所以,是時(shí)候開(kāi)始重新拾起。
3D繪畫是一個(gè)很復(fù)雜的數(shù)學(xué)物理綜合體,會(huì)涉及到很多基礎(chǔ)概念,了解了這些概念后才能進(jìn)行實(shí)際的開(kāi)發(fā)工作。
基礎(chǔ)概念
- WebGL不同于canvas,它是一個(gè)三維繪畫模擬技術(shù),在我們二維的顯示器里,其由深度決定前后的關(guān)系,根據(jù)遠(yuǎn)近進(jìn)行放大和縮小,通過(guò)坐標(biāo)變換(模型變換,視圖變換和投影變換)組合描繪出畫面的內(nèi)容。
- WebGL中,所謂的固定渲染管線(簡(jiǎn)單來(lái)說(shuō)是3D渲染所進(jìn)行的一連串的計(jì)算流程)是不存在的,所有的坐標(biāo)變換必須用自己全部完成,而且,這個(gè)計(jì)述坐標(biāo)變換的機(jī)制就叫做著色器(Shader),這樣可以由程序員控制的機(jī)制就叫做可編輯渲染管線。而著色器又有處理幾何圖形頂點(diǎn)的頂點(diǎn)著色器和處理像素的片段著色器兩種類型。
- 在HTML中,著色器是利用script標(biāo)簽進(jìn)行定義存放
<script id="vshader" type="x-shader/x-vertex">
※頂點(diǎn)著色器
</script>
<script id="fshader" type="x-shader/x-fragment">
※片段著色器
</script>
- 3D渲染變換繪制和演變使用的是4x4的矩陣,也就是行數(shù)和列數(shù)同為4的矩陣,在數(shù)學(xué)中我們都知道它叫方陣。實(shí)際3D渲染時(shí),準(zhǔn)備個(gè)各種坐標(biāo)變換的矩陣,然后相乘,將最終得到的矩陣傳給WebGL的頂點(diǎn)著色器。頂點(diǎn)著色器從傳過(guò)來(lái)的矩陣中,獲得模型的坐標(biāo),加工到畫面上顯示出來(lái)。也就是說(shuō),操作坐標(biāo)變換的矩陣,就可以決定模型在畫面上如何繪制。
- WebGL的世界里任何東西都是可以描畫,但是描畫的最基本東西也就是點(diǎn)、線段和三角形。頂點(diǎn),就是三維空間上存在的一個(gè)點(diǎn),包括X,Y,Z坐標(biāo)信息。頂點(diǎn)的連接順序是判斷3D繪畫多邊形的基準(zhǔn)的。順時(shí)針連接頂點(diǎn)的多邊形是在外側(cè),而逆時(shí)針連接的多邊形在內(nèi)側(cè)。為了減少處理,3D世界里看不到的東西不繪制,而這種機(jī)制就叫做遮擋剔除,如果我們?cè)O(shè)定了遮擋剔除,那么WebGL就只繪制外側(cè)看得見(jiàn)的東西,內(nèi)側(cè)所有多邊形就都不會(huì)再繪制。
基礎(chǔ)代碼
頁(yè)面初始化
<html>
<head>
<title>WebGL TEST</title>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
這段HTML代碼,純粹只是在頁(yè)面上放置一個(gè)canvas,我們將從這個(gè)canvas中獲取context,然后進(jìn)行WebGL初始化。
獲取context
首先獲取canvas對(duì)象并設(shè)置其大小
var c = document.getElementById('canvas');
c.width = 500;
c.height = 300;
獲取WebGL的context
// 兼容處理
var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
畫面初始化
WebGL的context和普通的canvas是一樣的,包含了繪畫相關(guān)的各種各樣的處理對(duì)象、函數(shù)、常量和屬性。例如:
// 使用指定常量顏色來(lái)清空畫面
gl.clear(gl.COLOR_BUFFER_BIT);
// 使用顏色值(RGBA)來(lái)清空畫面
gl.clearColor(0.0,0.0,0.0,1.0);
上面幾步簡(jiǎn)單的代碼塊就能夠完成一個(gè)幾步的WebGL使用,整個(gè)代碼運(yùn)行起來(lái)就是一個(gè)大小為500*300的黑色塊畫面。
認(rèn)識(shí)GLSL
我們已經(jīng)知道WebGL是無(wú)法利用固定渲染管線的,所以代替它的是可編輯渲染管線中的一種著色語(yǔ)言,叫做GLSL(OpenGL Shading Language)。
GLSL使用C語(yǔ)言為基礎(chǔ),并且有自己獨(dú)立的語(yǔ)法。WebGL編程難點(diǎn)之一也就是這個(gè)GLSL的使用。
- WebGL里有頂點(diǎn)著色器和片段著色器兩種著色器。無(wú)論哪一種都可以使用GLSL來(lái)編寫。頂點(diǎn)著色器和片段著色器是相互依賴的,缺一不可,并且首先被調(diào)用的是頂點(diǎn)著色器。我們可以把頂點(diǎn)相關(guān)的情報(bào)信息(位置、法線、紋理坐標(biāo)、顏色等)傳遞給頂點(diǎn)著色器去處理我們要繪制的頂點(diǎn)。而片段著色器則是決定畫面用什么顏色輸出。片段著色器英文是fragment,其實(shí)就是斷片,碎片的意思。而畫面上的像素實(shí)際上就是最小的斷片,總而言之,片段著色器操作的是顏色。
- GLSL編寫基礎(chǔ)
首先,不管是頂點(diǎn)著色器還是片段著色器,都必須定義一個(gè)main函數(shù),函數(shù)里記錄你要做的處理。而且,頂點(diǎn)著色器的話,必須要把頂點(diǎn)信息傳給一個(gè)叫做gl_Position的變量。例如:
// attibute修飾符是用來(lái)接收不同頂點(diǎn)傳來(lái)的不同信息
// vec*表示的是向量,*部分是一個(gè)2~4的數(shù)字,vec3表示的是一個(gè)3維的向量。其元素是浮點(diǎn)型
// position變量定義頂點(diǎn)信息
attribute vec3 position;
void main(void) {
gl_Position = position;
}
在WebGL中,頂點(diǎn)相關(guān)處理就是坐標(biāo)變換,模型變換、視圖變換和投影變換也就是頂點(diǎn)著色器的工作之一。一般來(lái)說(shuō),WebGL程序中,首先生成模型、視圖、投影的各個(gè)矩陣,然后進(jìn)行合并,最后將得到的坐標(biāo)變換的矩陣傳給頂點(diǎn)著色器。這時(shí),我們定義傳遞這些矩陣值:
attribute vec3 position;
// uniform修飾符是用來(lái)接收所有頂點(diǎn)一致的情報(bào)信息
// mat*表示的是方陣,可指定范圍2~4,mat4表示的是4x4的方陣。其元素是浮點(diǎn)型
uniform mat4 mvpMatrix;
void main(void) {
gl_Position = mvpMatrix * position;
}
頂點(diǎn)著色器與片段著色器的連接
GLSL里還有一個(gè)重要的修飾符,也就是varying修飾符,是用來(lái)連接頂點(diǎn)著色器和片段著色器之間的橋梁。
比如要把繪制的模型變成半透明,要怎么做?
方法雖然有很多,但是一般的做法是,向頂點(diǎn)里添加顏色的情報(bào)信息,然后通過(guò)操作顏色的透明度的變化來(lái)使模型半透明或者完全透明。這時(shí)候,如果想操作頂點(diǎn)里的顏色信息和畫面上的顏色信息的話,就需要向片段著色器里傳入一些必要的信息。首先是頂點(diǎn)著色器部分:
attribute vec4 position;
attribute vec4 color;
uniform mat4 mvpMatrix;
varying vec4 vColor
void main(void) {
vColor = color;
gl_Position = mvpMatrix * position;
}
接著,片段著色器接收通過(guò)varying修飾符所定義的變量vColor:
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
和頂點(diǎn)著色器中必需要把數(shù)據(jù)傳給gl_Position類似,片段著色器要把數(shù)據(jù)傳給gl_FragColor,只是與頂點(diǎn)著色器不同的是,片段著色器的gl_FragColor不是必須要賦值的。但是一般都會(huì)輸出一種什么顏色,所以gl_FragColor就變成必要的了。
頂點(diǎn)緩存
局部坐標(biāo)
?? ? ? 頂點(diǎn)最終在畫面上繪制的時(shí)候,要經(jīng)過(guò)模型坐標(biāo)變換,視圖坐標(biāo)變換和投影坐標(biāo)變換,這個(gè)已經(jīng)說(shuō)過(guò)好多遍了。但是,在使用坐標(biāo)情報(bào)之前,首先必須定義這些頂點(diǎn)群的構(gòu)成,否則就沒(méi)有辦法開(kāi)始了。定點(diǎn)群放到什么位置,就表現(xiàn)為坐標(biāo),一般叫做局部坐標(biāo)。局部坐標(biāo)就是模型的各個(gè)頂點(diǎn)相對(duì)于原點(diǎn)(x,y,z都為0)的坐標(biāo)。比如,一個(gè)局部坐標(biāo)為(1.0,0.0,0.0)的頂點(diǎn),x軸方向距離原點(diǎn)的距離是1.0。同樣,各個(gè)頂點(diǎn)都依次定義了局域坐標(biāo),這樣頂點(diǎn)的位置就形成了。
頂點(diǎn)保存
?? ? ? 這些頂點(diǎn)的局部坐標(biāo),必須在WebGL程序中進(jìn)行變換,然后傳給頂點(diǎn)著色器。在WebGL中,為了處理這些頂點(diǎn)的信息,并將這些頂點(diǎn)信息保存,則需要使用頂點(diǎn)緩存。緩存(buffer),是表示數(shù)據(jù)保存空間的一般的計(jì)算機(jī)用語(yǔ)。WebGL中還有幀緩存,索引緩存等各種緩存,但是不管哪種緩存,你只需要把它想成保存數(shù)據(jù)的一塊兒空間就行了。頂點(diǎn)緩存是其中的一種,就是用來(lái)保存頂點(diǎn)信息的,WebGL中的頂點(diǎn)緩存叫做VBO(vertex buffer object)。
頂點(diǎn)緩存和attribute
?? ? ? WebGL的程序中,先把頂點(diǎn)的信息保存到VBO中,接著,通知著色器哪個(gè)VBO和哪個(gè)attribute變量相關(guān),然后頂點(diǎn)著色器就可以正確的處理這些頂點(diǎn)了。根據(jù)前面的內(nèi)容,頂點(diǎn)緩存相關(guān)的處理的具體流程如下:
- 頂點(diǎn)的各種信息保存到數(shù)組里
- 使用WebGL的方法生成VBO
- 使用WebGL的方法將數(shù)組中的信息傳給VBO
- 頂點(diǎn)著色器中的attribute函數(shù)和VBO結(jié)合
VBO的生成過(guò)程中,首先在最初的時(shí)候必須把數(shù)據(jù)保存到數(shù)組中,因?yàn)轫旤c(diǎn)的信息(位置)中必須有x,y,z,所以數(shù)組的長(zhǎng)度必須是頂點(diǎn)數(shù)x3,這個(gè)時(shí)候需要注意,數(shù)組不可以使用多維數(shù)組,VBO的生成需要使用一維數(shù)組。準(zhǔn)備好保存頂點(diǎn)信息的數(shù)組之后,使用WebGL的context的方法生成VBO,當(dāng)然生成的時(shí)候VBO是空的,然后將頂點(diǎn)信息的數(shù)組傳給它。然后,比如把頂點(diǎn)著色器中的attribute函數(shù)和VBO關(guān)聯(lián)起來(lái)。上面也說(shuō)了,VBO中不是只能保存一種信息,位置情報(bào)以外的法線和顏色等信息存在的時(shí)候,要準(zhǔn)備合適的VBO,然后通知WebGL哪個(gè)VBO和哪個(gè)attribute變量相關(guān)聯(lián)。
矩陣計(jì)算和外部庫(kù)
矩陣計(jì)算
?? ? ? 矩陣的計(jì)算方法,也不是什么特別奇怪復(fù)雜的東西,如果數(shù)學(xué)好好學(xué)習(xí)的話,沒(méi)有基礎(chǔ)也可以進(jìn)行基本的矩陣計(jì)算。但是,如果不知道矩陣的加法和乘法運(yùn)算的話,要進(jìn)行稍微復(fù)雜一些的矩陣計(jì)算是非常難的。
矩陣的使用方法,并不是詳細(xì)的計(jì)算方法。特別是在3D開(kāi)發(fā)中,矩陣能夠做什么,通過(guò)什么運(yùn)算能得到什么樣的結(jié)果,主要是掌握矩陣的使用方法,這一點(diǎn)很重要。
外部庫(kù)
?? ? ? DirectX和OpenGL中,內(nèi)置了許多矩陣相關(guān)的處理,即使不使用外部庫(kù)也可以進(jìn)行矩陣計(jì)算。但是,WebGL中這些矩陣相關(guān)的計(jì)算是沒(méi)有的,可能為了簡(jiǎn)化吧,當(dāng)然,不是說(shuō)沒(méi)有辦法了,而是,矩陣相關(guān)的一切計(jì)算,都需要自己來(lái)處理。話雖如此,但是WebGL中的矩陣計(jì)算還是一個(gè)很大的問(wèn)題。數(shù)學(xué)好的人當(dāng)然是沒(méi)有問(wèn)題了,但是對(duì)于其他人數(shù)學(xué)不太好的人就太困難了。但是,不用怕,有很多使用JavaScript寫的矩陣計(jì)算的外部庫(kù),使用這些外部庫(kù)的話,就算自己不會(huì)矩陣計(jì)算,也可以進(jìn)行矩陣相關(guān)的處理,下面是其中的幾個(gè):
- glMatrix
http://code.google.com/p/glmatrix/ - mjs
http://code.google.com/p/webgl-mjs/ - Sylvester
http://sylvester.jcoglan.com/ - closure
http://code.google.com/p/closure-library/ - TDL
http://code.google.com/p/threedlibrary/ - minMatrix.js
http://wgld.org/j/minMatrix.js
著色器的編譯和連接
用個(gè)繪制多邊形的例子來(lái)開(kāi)始吧,首先,確認(rèn)一下繪制的步驟:
- 從HTML中獲取canvas對(duì)象
- 從canvas中獲取WebGL的context
- 編譯著色器
- 準(zhǔn)備模型數(shù)據(jù)
- 頂點(diǎn)緩存(VBO)的生成和通知
- 坐標(biāo)變換矩陣的生成和通知
- 發(fā)出繪圖命令
- 更新canvas并渲染
步驟1、2和著色器的定義代碼之前的“基礎(chǔ)代碼”和“認(rèn)識(shí)GLSL”已經(jīng)學(xué)習(xí)過(guò),再次整理一下:
<html>
<head>
<title>WebGL TEST</title>
</head>
<body>
<canvas id="canvas"></canvas>
<!-- ※頂點(diǎn)著色器 -->
<script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script>
<!-- ※片段著色器 -->
<script id="fs" type="x-shader/x-fragment">
void main(void){
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
<script>
//首先獲取canvas對(duì)象并設(shè)置其大小
var c = document.getElementById('canvas');
c.width = 500;
c.height = 300;
// 從canvas中獲取WebGL的context
var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
</script>
</body>
</html>
編譯著色器
?? ? ? 編譯也不需要什么特別的編譯器,只需要調(diào)用WebGL內(nèi)部的函數(shù)就可以進(jìn)行編譯了。準(zhǔn)備一個(gè)函數(shù),從著色器的編譯,到實(shí)際著色器的生成這一連串的流程,都在這一個(gè)函數(shù)中來(lái)完成。下面是這個(gè)函數(shù)的代碼:
function create_shader(id){
// 用來(lái)保存著色器的變量
var shader;
// 根據(jù)id從HTML中獲取指定的script標(biāo)簽
var scriptElement = document.getElementById(id);
// 如果指定的script標(biāo)簽不存在,則返回
if(!scriptElement){return;}
// 判斷script標(biāo)簽的type屬性
switch(scriptElement.type){
// 頂點(diǎn)著色器的時(shí)候
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break;
// 片段著色器的時(shí)候
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default :
return;
}
// 將標(biāo)簽中的代碼分配給生成的著色器
gl.shaderSource(shader, scriptElement.text);
// 編譯著色器
gl.compileShader(shader);
// 判斷一下著色器是否編譯成功
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
// 編譯成功,則返回著色器
return shader;
}else{
// 編譯失敗,彈出錯(cuò)誤消息
alert(gl.getShaderInfoLog(shader));
}
}
程序?qū)ο蟮纳珊瓦B接
?? ? ? 使用varying修飾符定義的變量,可以從頂點(diǎn)著色器向片段著色器中傳遞數(shù)據(jù)。其實(shí),實(shí)現(xiàn)從一個(gè)著色器向另一個(gè)著色器傳遞數(shù)據(jù)的,不是別的,就是程序?qū)ο蟆3绦驅(qū)ο笫枪芾眄旤c(diǎn)著色器和片段著色器,或者WebGL程序和各個(gè)著色器之間進(jìn)行數(shù)據(jù)的互相通信的重要的對(duì)象。
那么,生成程序?qū)ο螅阎鱾鹘o程序?qū)ο螅缓筮B接著色器,將這些處理函數(shù)化:
function create_program(vs, fs){
// 程序?qū)ο蟮纳?
var program = gl.createProgram();
// 向程序?qū)ο罄锓峙渲?
gl.attachShader(program, vs);
gl.attachShader(program, fs);
// 將著色器連接
gl.linkProgram(program);
// 判斷著色器的連接是否成功
if(gl.getProgramParameter(program, gl.LINK_STATUS)){
// 成功的話,將程序?qū)ο笤O(shè)置為有效
gl.useProgram(program);
// 返回程序?qū)ο?
return program;
}else{
// 如果失敗,彈出錯(cuò)誤信息
alert(gl.getProgramInfoLog(program));
}
}
VBO的生成
?? ? ?生成VBO的時(shí)候使用WebGL的createBuffer函數(shù),這個(gè)函數(shù)就是用來(lái)生成緩存的。但是這個(gè)函數(shù)并不是用來(lái)直接生成VBO的,它只是生成了一個(gè)緩存對(duì)象,根據(jù)它里面保存的內(nèi)容不同,用途也是不用的。
要操作緩存,首先必須跟WebGL進(jìn)行綁定,就是說(shuō),要向“緩存”這個(gè)“光盤”中寫入數(shù)據(jù)的時(shí)候,必須連接到WebGL這個(gè)“光驅(qū)”上。
綁定了緩存之后,使用bufferData函數(shù)來(lái)向緩存中寫入數(shù)據(jù),把這些處理寫成一個(gè)函數(shù),就是下面這樣:
function create_vbo(data){
// 生成緩存對(duì)象
var vbo = gl.createBuffer();
// 綁定緩存
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
// 向緩存中寫入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
// 將綁定的緩存設(shè)為無(wú)效
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// 返回生成的VBO
return vbo;
}
這個(gè)函數(shù),接受一個(gè)矩陣作為參數(shù),最后返回生成的VBO。首先使用createBuffer生成緩存對(duì)象,接著綁定緩存,然后寫入數(shù)據(jù)。
綁定緩存的時(shí)候使用bindBuffer函數(shù),這個(gè)函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)是緩存的類型,第二個(gè)參數(shù)是指定緩存對(duì)象。將第一個(gè)參數(shù)指定為gl.ARRAY_BUFFER就可以生成VBO。
另外,bufferData函數(shù)的第二個(gè)參數(shù)中出現(xiàn)的Float32Array對(duì)象,是javascript的類型數(shù)組,和一般的Array對(duì)象類似,是處理浮點(diǎn)型小數(shù)的時(shí)候使用的數(shù)組對(duì)象。3D世界里小數(shù)的精確度非常重要,所以使用類型數(shù)組來(lái)傳遞數(shù)據(jù)。而第三個(gè)參數(shù)中的gl.STATIC_DRAW這個(gè)常量,定義了這個(gè)緩存中內(nèi)容的更新頻率。VBO的話,模型數(shù)據(jù)基本上就是直接這么反復(fù)用,所以使用這個(gè)常量。
可以綁定WebGL的緩存,一次只能綁定一個(gè),所以要操作其他的緩存的時(shí)候,必須要綁定相應(yīng)的緩存。所以在函數(shù)的最后,再次使用bindBuffer函數(shù),設(shè)定第二個(gè)參數(shù)為null,來(lái)將上次的綁定無(wú)效化,這是為了防止WebGL中的緩存一致保留,而出現(xiàn)和預(yù)想不一致的情況。
坐標(biāo)變換矩陣的基本功能
?? ? ?進(jìn)行基本的3D渲染的時(shí)候,需要準(zhǔn)備3個(gè)坐標(biāo)變換矩陣。
第一個(gè)是模型變換矩陣,DirectX中叫做世界變換矩陣。模型變換矩陣影響的是所繪制的模型,模型的位置,模型的旋轉(zhuǎn),模型的放大和縮小等相關(guān)的情況。
第二個(gè)是視圖變換矩陣,簡(jiǎn)單來(lái)說(shuō),就是定義拍攝3D空間的鏡頭(攝像機(jī)),決定了鏡頭的位置,鏡頭的參考點(diǎn),鏡頭的方向等。
第三個(gè)是投影變換矩陣,這個(gè)坐標(biāo)變換定義了屏幕的橫豎比例,剪切的領(lǐng)域等,另外獲取遠(yuǎn)近法則的效果也需要用這個(gè)變換矩陣。
根據(jù)這些內(nèi)容,差不多知道了需要對(duì)矩陣進(jìn)行哪些操作。使用minMatrix.js可以對(duì)矩陣進(jìn)行基本的操作,來(lái)看一下minMatrix.js都能完成哪些操作吧。
- minMatrix.js的基本功能
minMatrix.js包含矩陣的生成和矩陣的基本操作,minMatrix.js的核心是一個(gè)叫做matIV的對(duì)象,通過(guò)這個(gè)對(duì)象可以進(jìn)行所有的矩陣操作,使用minMatrix.js來(lái)操作矩陣的時(shí)候,首先,需要生成一個(gè)matIV對(duì)象:
var m = new matIV();
像上面這樣,變量m就是matIV對(duì)象的一個(gè)實(shí)例,通過(guò)m.方法名可以調(diào)用matIV對(duì)象中存在的方法。
下面,列舉一下minMatrix.js中定義的matIV對(duì)象的方法:
.create
函數(shù): matIV.create()
參數(shù): 無(wú)
返回值: 矩陣
生成一個(gè)4x4的方陣,里面包含16個(gè)元素,其實(shí)是一個(gè)Float32Array對(duì)象,所有的元素都被初始化為0
.identity
函數(shù): matIV.identity(dest)
參數(shù): dest > 初始化的矩陣
返回值: 初始化后的矩陣
將接收的矩陣參數(shù)進(jìn)行初始化并返回
.multiply
函數(shù): matIV.multiply(mat1,mat2,dest)
參數(shù): mat1 > 相乘的原始矩陣
參數(shù): mat2 > 作為乘數(shù)的矩陣
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
mat1在左,mat2在右,相乘后的結(jié)果保存到dest中
.scale
函數(shù): matIV.scale(mat,vec,dest)
參數(shù): mat > 原始矩陣
參數(shù): vec > 縮放向量
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
模型變換中的放大縮小,mat是原始矩陣,vec是X,Y,Z的各個(gè)縮放值組成的向量,最后的計(jì)算結(jié)果保存在dest中
.translate
函數(shù): matIV.translate(mat,vec,dest)
參數(shù): mat > 原始矩陣
參數(shù): vec > 表示從原點(diǎn)開(kāi)始移動(dòng)一定距離的向量
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
模型變換中的坐標(biāo)移動(dòng),mat是原始矩陣,vec是X,Y,Z的各個(gè)方向上的移動(dòng)量組成的向量,最后將計(jì)算結(jié)果保存到dest中
.rotate
函數(shù): matIV.rotate(mat,angle,axis,dest)
參數(shù): mat > 原始矩陣
參數(shù): angle > 旋轉(zhuǎn)角度
參數(shù): axis > 旋轉(zhuǎn)軸的向量
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
模型變換中的旋轉(zhuǎn),mat是原始矩陣,angle是旋轉(zhuǎn)角度,axis是旋轉(zhuǎn)軸向量,最后將計(jì)算結(jié)果保存到dest中
.lookAt
函數(shù): matIV.lookAt(eye,center,up,dest)
參數(shù): eye > 鏡頭位置向量
參數(shù): center > 鏡頭參考點(diǎn)的向量
參數(shù): up > 鏡頭的方向向量
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
視圖變換矩陣的生成,eye是鏡頭在三維空間中的位置,center是這個(gè)鏡頭的參考點(diǎn),up是鏡頭的方向向量,最后將計(jì)算結(jié)果保存到dest中
.perspective
函數(shù): matIV.perspective(fovy,aspect,near,far,dest)
參數(shù): fovy > 視角
參數(shù): aspect > 屏幕的寬高比例
參數(shù): near > 近截面的位置
參數(shù): far > 遠(yuǎn)截面的位置
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
投影變換矩陣的生成,這里生成的是一般被稱為[透視射影]的投影變換矩陣,包含遠(yuǎn)近法則。fovy是視角,aspect是屏幕的橫豎比例,near是近截面的位置(必須是大于0的數(shù)值),far遠(yuǎn)截面的位置(任意數(shù)值),最后將計(jì)算結(jié)果保存到dest中
.transpose
函數(shù): matIV.transpose()
參數(shù): mat > 原始矩陣
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
矩陣的行列互換,將計(jì)算結(jié)果保存到dest中
.inverse
函數(shù): matIV.inverse(mat,dest)
參數(shù): mat > 原始矩陣
參數(shù): dest > 用來(lái)保存計(jì)算結(jié)果的矩陣
求矩陣的逆矩陣,mat是原始矩陣,求的的逆矩陣保存到dest中
- 矩陣變換的流程
使用minMatrix.js的話,可以操作矩陣,那么先來(lái)確認(rèn)一下操作順序。
模型變換也好,視圖變換,投影變換也好,如果不先生成矩陣的話,就什么也做不了。所以首先執(zhí)行matIV.create生成矩陣,然后通過(guò)matIV.identity來(lái)初始化矩陣,代碼如下:
// 生成matIV對(duì)象
var m = new matIV();
// 矩陣生成及初始化
var Matrix = m.identity(m.create());
(未完待續(xù))
注:學(xué)習(xí)內(nèi)容來(lái)自于https://wgld.org/