Three.js是一個比較偉大的webgl開源庫,它簡化了浏覽器3D編程,使得使用JavaScript在浏覽器中創建復雜的場景變得容易很多。Github上眾多webgl demo令我興奮不已,躍躍欲試。由於這個庫還處在開發階段,因此資料非常匮乏,愛好者大部分時間不得不通過閱讀該庫的源碼進行學習,我現在也准備這樣做。
這是第一篇筆記,先從最基礎的核心(Core)對象開始。
Core::Vector2 該構造函數用來創建一個表示二維向量的對象
復制代碼 代碼如下:
THREE.Vector2 = function ( x, y ) {
this.x = x || 0;
this.y = y || 0;
};
Vector2對象的功能函數采用定義構造函數的原型對象來實現,形如:
復制代碼 代碼如下:
THREE.Vector2.prototype = {
constructor: THREE.Vector2,
set: function ( x, y ) {
this.x = x;
this.y = y;
return this;
},
copy: function ( v ) {
this.x = v.x;
this.y = v.y;
return this;
},
...... // 更多的函數
};
函數set(x,y)用以指定向量的值,調用者本身的x,y值被影響了,而該方法本身又返回調用者本身,這種情況很常見,以下不再說明。通過文字能夠表述清楚功能的函數不再引用源代碼,這一點以下也不再說明。
函數copy(v)用來將向量v復制進調用者。
函數add(a,b)和函數sub(a,b)分別表示對向量a,b相加和相減。
函數addSelf(v)和subSelf(v)分別表示對調用者本身加上或減去向量v。
函數multiplyScale(s)和divideScale(s)分別表示對調用者本身乘以或除以s。
函數lerpSelf(v,alpha)將調用者向v所指的方向旋轉alpha,當alpha為1時,調用者最終等於v,而當alpha=0時,調用者還等於原來。
復制代碼 代碼如下:
lerpSelf: function ( v, alpha ) {
this.x += ( v.x - this.x ) * alpha;
this.y += ( v.y - this.y ) * alpha;
return this;
},
函數negate()對調用者取反。
函數dot(v)返回float類型的調用者和向量v的點乘。
函數lengthSq()和函數length()返回float類型的調用者長度平方或長度。
函數normalize()將調用者本身歸一化。
函數distanceToSquared(v)和distanceTo(v)將返回調用者和向量v的距離。這裡的距離其實是兩向量起點都在原點時,終點之間的距離,也就是向量this-v的長度。
函數setLength(s)將向量的長度縮放至為s,方向不變。
函數equals(v)判斷調用者與向量v的值是否相同。
函數isZero()判斷調用者是否是零向量。
函數clone()返回一個與調用者值一樣的新向量,相當於將其復制出去,注意與copy(v)的區別。
Core::Vector3 該構造函數創建一個表示三維向量的對象
復制代碼 代碼如下:
THREE.Vector3 = function ( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
};
三維向量和二維向量有許多共通之處,比如set,add,dot,length,clone等,此處盡數略去,只記錄三維向量比二維向量多出的部分函數。
函數setX(x),setY(y)和setZ(z)用來單獨設置某一分量的值。
函數cross(a,b)和crossSelf(v)分別使調用者變為a,b的叉乘或者調用者本身與v的叉乘。叉乘是一個向量,垂直於參與叉乘的兩個向量並呈右手螺旋法則。
函數getPositionFromMatrix(m),getRotationFromMatrix(m),getScaleFromMatrix(m)從4×4的模型矩陣中提取位置分量,旋轉分量和縮放分量。模型矩陣表示了一系列平移、旋轉、縮放變換的疊加效果。(這裡第二個函數出現在文檔中,在源碼中被另外兩個函數代替了,也許還沒來得及更新)。
函數angleTo(v)計算調用者和向量v的夾角。
Core::Vector4 該構造函數創建一個表示四維向量的對象
復制代碼 代碼如下:
THREE.Vector4 = function ( x, y, z, w ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
this.w = ( w !== undefined ) ? w : 1;
};
四維向量用來表示齊次坐標,其函數和Vector2,Vector3中的函數功能重合,僅僅是多一個分量而已,這裡不再記錄。
Core::Matrix3 該構造函數創建一個表示3×3矩陣的對象
THREE.Matrix3 = function () {
this.elements = new Float32Array(9);
};
3×3矩陣有9個元素,存儲在矩陣對象的屬性elements中,elements是一個數組。
函數getInverse(m)返回矩陣m的逆矩陣,同時改變調用者本身。
函數transpose()轉置調用者。
函數transposeToArray(r)將調用者轉置進數組r而不改變自身。(這個地方似乎源碼錯了,var m=this.m應該為var m=this.elements。)
Core::Matrix4 該構造函數創建一個表示4×4矩陣的對象,4×4矩陣在三維圖形學中非常重要,模型矩陣、視圖矩陣和投影矩陣都是這樣的矩陣。
復制代碼 代碼如下:
THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
this.elements = new Float32Array( 16 );
this.set(
( n11 !== undefined ) ? n11 : 1, n12 || 0, n13 || 0, n14 || 0,
n21 || 0, ( n22 !== undefined ) ? n22 : 1, n23 || 0, n24 || 0,
n31 || 0, n32 || 0, ( n33 !== undefined ) ? n33 : 1, n34 || 0,
n41 || 0, n42 || 0, n43 || 0, ( n44 !== undefined ) ? n44 : 1
);
};
在Matrix3對象中出現的幾個函數在Matrix4中有相同的作用,這裡也略去。
函數identity()將對象重置為單位陣。
函數lookAt(eye,center,up)將對象設定為一個視圖矩陣,參數都是Vector3對象,該矩陣只會用到eye和center的相對位置。該視圖矩陣表示,攝像機在eye位置看向center位置,且向上的向量(這一點稍後解釋)為up時的視圖矩陣。視圖矩陣又可以看做攝像機的模型矩陣,所以該函數產生的矩陣又可以表示以下變換:將物體從原點平移至位置center-eye,再將其旋轉至向上的向量為up。向上的向量up用來固定相機,可以想象當相機固定在一點,鏡頭朝向固定方向的時候,還是可以在一個維度裡自由旋轉的,up向量固定相機的這個維度。
復制代碼 代碼如下:
lookAt: function ( eye, target, up ) {
var te = this.elements;
var x = THREE.Matrix4.__v1; // 空Vector3對象,下同
var y = THREE.Matrix4.__v2;
var z = THREE.Matrix4.__v3;
z.sub( eye, target ).normalize();
if ( z.length() === 0 ) {
z.z = 1;
}
x.cross( up, z ).normalize();
if ( x.length() === 0 ) {
z.x += 0.0001;
x.cross( up, z ).normalize();
}
y.cross( z, x );
te[0] = x.x; te[4] = y.x; te[8] = z.x;
te[1] = x.y; te[5] = y.y; te[9] = z.y;
te[2] = x.z; te[6] = y.z; te[10] = z.z;
return this;
},
函數multiply(a,b),multiplySelf(v)和multiplyToArray(a,b,r)將兩個矩陣相乘。
函數multiplyScale(s)將對象所有16個元素都乘以s。
函數multiplyVector3(v)和multiplyVector4(v)將對象矩陣左乘四維行向量,返回vector3和vector4類型的行向量。如果對象矩陣是模型視圖矩陣,輸入的向量是點位置信息,則輸出的向量則是經過模型變換和相機變換後,該點相對於相機的位置。輸入vector3類型向量時,自動補足為齊次坐標,返回時再砍掉第四個分量成為普通坐標。
函數rotateAxis(v)使用對象矩陣左上角的3×3子矩陣左乘行向量v,得到一個新的行向量並歸一化,返回這個新行向量。該函數同時更新了向量v的值。模型視圖矩陣左上角3×3的子矩陣包含了模型矩陣中的旋轉信息,將該子矩陣左乘一個向量,得到的新向量實際上就是原向量經過旋轉(該旋轉效果來自於模型矩陣)得到的。因此該函數名為rotateAxis。
復制代碼 代碼如下:
rotateAxis: function ( v ) {
var te = this.elements;
var vx = v.x, vy = v.y, vz = v.z;
v.x = vx * te[0] + vy * te[4] + vz * te[8];
v.y = vx * te[1] + vy * te[5] + vz * te[9];
v.z = vx * te[2] + vy * te[6] + vz * te[10];
v.normalize();
return v;
},
函數crossVector(v)計算矩陣對象(調用者)和v的叉乘,實際上就是對象矩陣左乘四維行向量v,返回向量。這個具體是做什麼的,我還沒弄明白。
復制代碼 代碼如下:
crossVector: function ( a ) {
var te = this.elements;
var v = new THREE.Vector4();
v.x = te[0] * a.x + te[4] * a.y + te[8] * a.z + te[12] * a.w;
v.y = te[1] * a.x + te[5] * a.y + te[9] * a.z + te[13] * a.w;
v.z = te[2] * a.x + te[6] * a.y + te[10] * a.z + te[14] * a.w;
v.w = ( a.w ) ? te[3] * a.x + te[7] * a.y + te[11] * a.z + te[15] * a.w : 1;
return v;
},
函數determinant()計算矩陣的行列式值。
函數flattenToArray(flat)和函數flattenToArrayOfset(flat,offset)將矩陣轉存到一維數組中,前一個函數從flat[0]存儲到flat[15],後一個函數允許指定開始存儲的位置,從flat[offset]存儲到flat[offset+15]。
函數getPosition()和函數setPosition()用來獲取或設置矩陣對象的位置分量。正如旋轉分量存儲在左上角3×3的子矩陣中,位置分量存儲在第四行前三個分量上,即element[12], element[13], element[14]中。
函數getColumeX(),getColumeY(),getColumeZ()分別提取左上角3×3子矩陣的三列。
函數compose(translate,rotation,scale)將對象矩陣設置為由vector3類型translate對象表示的平移、由matrix3類型rotation對象表示的旋轉、由vector3類型scale對象表示的縮放這三個變換組合到一起的變換矩陣。實際上就是講其直接填充到模型矩陣的相應子空間。
函數decompose(translate,rotation,scale)將矩陣對象拆開到三個對象中,和上一個函數正好相反。
函數extractPosition(m)和extractRotation(m)將矩陣對象m中表示位置或旋轉的分量抽取到調用者對象中,比如兩個物體經過多次各不相同的變換,只需要一個物體的模型視圖矩陣extractRotation另一個物體的模型視圖矩陣,則調用者就和另外一個物體保持著變換之處相同的旋轉方位。
函數translate(v)是模型矩陣最基本的變換之一:平移變換,將模型矩陣從屬的物體平移向量v。
函數rotateX(angle),rotateY(angle),rotateZ(angle)分別將模型矩陣從屬的物體繞X,Y,Z軸旋轉角度angle。
函數rotateByAxis(axis, angle)將模型矩陣從屬的物體繞一個任意軸axis旋轉角度angle,這是上面兩條所涉及的變換的多次疊加(疊加參數由當前位置和axis參數決定),我在《模型視圖矩陣和投影矩陣:webgl筆記(1)》中曾討論到繞任意軸旋轉的問題。
這裡不應該有一個scale(s)函數嗎?可是我在源碼中沒找到。
函數makeTranslate(x,y,z),makeRotationX(theta),makeRotationY(theta),makeRotationZ(theta),makeRotationAxis(axis,angle),makeScale(s)函數將對象矩陣直接重置為單位陣經過一次平移、或繞某軸旋轉、或單純某次縮放後的矩陣。該函數更新對象本身的值,而且更新的結果與對象之前的值毫無關聯(這也是make前綴函數的特點)。
函數makeFrustum(...),makePerspective(...),makeOrthographic(...)也是用來初始化新矩陣,具體含義到相機類裡面再討論,我想相機類的構造函數裡一定會調用這些函數的。
函數clone()將矩陣對象復制出來並返回。
Core::Face3 該函數創建一個三角形平面對象
復制代碼 代碼如下:
THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) {
this.a = a;
this.b = b;
this.c = c;
this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
this.vertexNormals = normal instanceof Array ? normal : [ ];
this.color = color instanceof THREE.Color ? color : new THREE.Color();
this.vertexColors = color instanceof Array ? color : [];
this.vertexTangents = [];
this.materialIndex = materialIndex;
this.centroid = new THREE.Vector3();
};
對象的a,b,c值是三個頂點的索引(後面會說到,Mesh對象中將所有點存儲為一個數組);顧名思義normal是法線;color是顏色;materialIndex是頂點材質索引:這幾個參數即可以傳入vector3類型又可以傳入數組類型。
clone(x)方法返回一個新的,具有相同值的對象。
Core::Face4 該函數創建一個四個頂點的面,和Face3幾乎一樣,略去。
Core::Math THREE.Math是一個“靜態類”,沒有構造函數因此也不需要通過new關鍵字初始化。該類提供一些必要的數學工具。
函數clamp(x,a,b)將x夾在區間[a,b]中。clampBottom(x,a)的作用類似,只不過只夾一邊。
函數mapLinear(x,a1,a2,b1,b2)計算出一個值y,使得點(x,y)落在(a1,a2)和(b1,b2)連成的直線上。
復制代碼 代碼如下:
mapLinear: function ( x, a1, a2, b1, b2 ) {
return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
},
函數random16(),randInt(low,high),randFloat(low,high),randFloatSpread(range)分別產生[0,1]區間的16位隨機浮點數,[low,high]區間隨機整數,[low,high]區間隨機浮點數,[-range/2,range/2]區間隨機浮點數。
函數sigh(x)根據x的符號返回+1或-1。
Core::Clock 該構造函數創建時鐘(確切的說是秒表)對象
復制代碼 代碼如下:
THREE.Clock = function ( autoStart ) {
this.autoStart = ( autoStart !== undefined ) ? autoStart : true;
this.startTime = 0;
this.oldTime = 0;
this.elapsedTime = 0;
this.running = false;
};
函數start()和stop()用來開始計時或停止計時。
函數getDelta()返回調用該函數時距離上一次調用該函數時的時間長度,如果是第一次調用該函數,則返回此時距離開始計時時的時間長度。如果autoStart值為真,若在調用getDelta()函數時尚未調用start()函數或者已經調用過stop()函數,則自動開始計時並返回0。如果autoStart()值為假,則在調用start()之前或stop()之後,調用getDelta()返回0。
函數getElapsedTime()返回調用該函數時距離開始計時時的時間。
Core::Color 該構造函數構造一個表示顏色的對象
復制代碼 代碼如下:
THREE.Color = function ( hex ) {
if ( hex !== undefined ) this.setHex( hex );
return this;
};
函數setHex(hex)以十六進制序列設置對象的r,g,b屬性。實際上在對象中,最終是以這三個屬性存儲顏色的。
復制代碼 代碼如下:
setHex: function ( hex ) {
hex = Math.floor( hex );
this.r = ( hex >> 16 & 255 ) / 255;
this.g = ( hex >> 8 & 255 ) / 255;
this.b = ( hex & 255 ) / 255;
return this;
},
函數setRGB(r,g,b)和setHSV(h,s,v)以RGB值或HSV值設置對象。
函數getHex()返回16進制顏色值。
函數copyGammaToLinear(color),copyLinearToGamma(color)將color的rgb值分別平方或開方,賦給調用者對象。
函數convertGammaToLinear()和convertLinearToGamma()分別對調用者自身的rgb值平方或開放。