「C3dl」修訂間的差異
出自 MozTW Wiki
(c3dl introduction) |
(→觀景窗 Scene) |
||
(未顯示同一使用者於中間所作的 27 次修訂) | |||
行 1: | 行 1: | ||
− | 請見 | + | 請見 [http://zenit.senecac.on.ca/wiki/index.php/Canvas3D_JS_Library Canvas3D JS Library] |
== 簡介 == | == 簡介 == | ||
− | Canvas 3D JS Library (C3DL) 是 javascript 函式庫,也就是裡頭提供的全部是 Javascript, 必須安裝 | + | Canvas 3D JS Library (C3DL) 是 javascript 函式庫,也就是裡頭提供的全部是 Javascript, 必須安裝 [https://addons.mozilla.org/en-US/firefox/addon/7171 Canvas3D extension of firefox], [http://www.c3dl.org/ c3dl 程式碼在此]。主要目的是讓你在 Firefox/Mozilla 平台用 Canvas/OpenGL 的方式撰寫 3D 的網路應用。 |
C3DL 提供一系列的數學、景觀、及3D物件類別,讓你在用 Canvas 會更有彈性,當然主要就是要縮短開發時間。 | C3DL 提供一系列的數學、景觀、及3D物件類別,讓你在用 Canvas 會更有彈性,當然主要就是要縮短開發時間。 | ||
+ | |||
+ | == 本專案開發人員 == | ||
+ | * Catherine Leung | ||
+ | * Mark Paruzel (CodeBot) | ||
+ | * Andrew Smith | ||
+ | * Chris Bishop (Javascript) | ||
+ | * Andor Salga | ||
+ | |||
+ | == 有用的連結 == | ||
+ | * [http://www.c3dl.org 我們的網站] | ||
+ | * [http://littlesvr.ca/canvas3d/blog/ 我們的 blog] | ||
+ | * Our SVN Repo: svn://cdot.senecac.on.ca/canvas3d | ||
+ | * [http://vlad.off.net/hg/canvas3d/ 在 Vlad's hg repo 中的 canvas3d 源碼] | ||
+ | * [http://www.opengl.org/documentation OpenGL 文件] | ||
+ | * [http://nehe.gamedev.net/ Nehe 的 OpenGL 教學] | ||
+ | * [http://shape.cs.princeton.edu/benchmark/ .off (object file format) format info] | ||
+ | |||
+ | == 類別繼承圖 == | ||
+ | * [http://img126.imageshack.us/img126/3032/layoutqe4.jpg Canvas 3D API 類別繼承圖] | ||
+ | |||
+ | == 數學運算 == | ||
+ | 我的感覺是這份 c3dl 並不是非常有效率,可以稍微修正寫法,譬如參考 [http://sourceforge.net/projects/papervision3d paperVision3D] | ||
+ | |||
+ | === Vector 向量類別 === | ||
+ | 一個向量基本上就是在 3D 世界的 X, Y, Z 三個軸的座標系統描述一個「具備大小的方向」。3D 數學存在各種不同的座標系統,離開向量類別的封裝則不復存在所謂的 3D。向量類別具有下列的成員; | ||
+ | |||
+ | * isValidVector(vecArr) - '''判斷參數是否為一有效的向量''' | ||
+ | * copyVector(srcVec) - '''從 srcVec 複製並傳回''' | ||
+ | * copyVectorContents(srcVec, destVec) - '''效果相當於(不等於,因為有多作判斷是否為有效向量) destVec = copyVector(srcVec);''' | ||
+ | * makeVector(newX, newY, newZ) - '''copyVector() 就是用這個函數實作向量複製''' | ||
+ | * normalizeVector(vec) - '''計算與向量相同方向但長度為一的「單位向量」並傳回''' | ||
+ | * vectorDotProduct(vecOne, vecTwo) - '''傳回兩向量的內積(點積),其值為純量''' | ||
+ | * vectorCrossProduct(vecOne, vecTwo, dest) - '''將 vecOne 與 vecTwo 兩個向量做外積(叉積)後指定給 dest)''' | ||
+ | * vectorLength(vec) - '''計算並傳回向量的長度(相當於與自己的內積開根號)''' | ||
+ | * vectorLengthSq(vec) - '''計算向量長度的平方,相比於直接利用長度的運算上,少了一個根號後再平方的無用計算''' | ||
+ | * addVectors(vecOne, vecTwo, dest) - '''相當於 dest = vecOne + vecTwo''' | ||
+ | * subtractVectors(vecOne, vecTwo, dest) - '''相當於 dest = vecOne - vecTwo''' | ||
+ | * multiplyVector(vec, scalar, dest) - '''相當於 dest = vec * scalar; 效果相當於將向量放大(scalar 小於1的正數則為縮小,負數則為反向)''' | ||
+ | * divideVector(vec, scalar, dest) - '''相當於 dest = vec / scalar; 效果相當於將向量縮小(scalar 小於1的正數則為放大,負數則為反向)''' | ||
+ | * multiplyVectorByVector(vecOne, vecTwo, dest) - '''既非內積也非外積,而是相當於 dest = [X1*X2, Y1*Y2, Z1*Z2]; 的乘法''' | ||
+ | * isVectorEqual(vecOne, vecTwo) - '''判斷兩向量是否相等''' | ||
+ | * isVectorZero(vec) - '''判斷向量長度是否為 0,正確的說法: 判斷是否極接近 0,每個軸向誤差在 0.00001 以內''' | ||
+ | * getAngleBetweenVectors(vecOne, vecTwo) - '''計算兩向量間的夾角''' | ||
+ | |||
+ | === Matrix 矩陣類別 === | ||
+ | c3dl 的矩陣在數學上是一個 4x4 的二維矩陣,但是在 Javascript 實作上是用一維陣列來表達,而不是二維陣列,其索引值如下: | ||
+ | |||
+ | +- -+ | ||
+ | | 0, 4, 8, 12 | | ||
+ | | 1, 5, 9, 13 | | ||
+ | | 2, 6, 10, 14 | | ||
+ | | 3, 7, 11, 15 | | ||
+ | +- -+ | ||
+ | |||
+ | * isValidMatrix(mat) - '''判斷是否為有效的矩陣''' | ||
+ | * makeIdentityMatrix() - '''產生單位矩陣,也就是斜對角都為 1 其餘為 0 的矩陣''' | ||
+ | * makeZeroMatrix() - '''產生全部是0 的矩陣''' | ||
+ | * makeMatrix(e00, e01, e02, e03, e10, e11, e12, e13, e20, e21, e22, e23, e30, e31, e32, e33) - '''利用參數產生矩陣,其索引值順序如上述,或由此處的宣告亦可得知''' | ||
+ | * matricesEqual(matrix1, matrix2) - '''判斷兩矩陣是否相等''' | ||
+ | * makePoseMatrix(vecLeft, vecUp, vecFrwd, vecPos) - '''位置矩陣,通常用來處理「手勢」,效果如下''': | ||
+ | <pre> | ||
+ | +- -+ | ||
+ | | Left.x, Up.x, Fwd.x, Pos.x | | ||
+ | | Left.y, Up.y, Fwd.y, Pos.y | | ||
+ | | Left.z, Up.z, Fwd.z, Pos.z | | ||
+ | | 0.0, 0.0, 0.0, 1.0 | | ||
+ | +- -+ | ||
+ | </pre> | ||
+ | * transposeMatrix(mat) - '''傳回轉置矩陣''': 型如 | ||
+ | <pre> | ||
+ | +- -+ | ||
+ | | A, B, C, D | | ||
+ | | E, F, G, H | | ||
+ | | I, J, K, L | | ||
+ | | M, N, O, P | | ||
+ | +- -+ | ||
+ | </pre> | ||
+ | 轉置結果為: | ||
+ | <pre> | ||
+ | +- -+ | ||
+ | | A, E, I, M | | ||
+ | | B, F, J, N | | ||
+ | | C, G, K, O | | ||
+ | | D, H, L, P | | ||
+ | +- -+ | ||
+ | </pre> | ||
+ | * inverseMatrix(mat) - '''計算並傳回 mat 的反矩陣,使得 res * mat = I, 其中 I 是單位矩陣,參考 [http://mathworld.wolfram.com/MatrixInverse.html 反矩陣]''' | ||
+ | * matrixDeterminant(mat) - '''計算並傳回 mat 的行列式,通常用來算面積或體積,其值為純量,參考 [http://en.wikipedia.org/wiki/Determinant 行列式]''' | ||
+ | * matrixAdjoint(mat) - '''不知道怎麼翻,伴隨矩陣?共軛矩陣?參考 [http://www.ee.ic.ac.uk/hp/staff/dmb/matrix/property.html#adjoint adjoint matrix]''' | ||
+ | * multiplyMatrixByScalar(mat, scalar) - '''每個元素都乘以 scalar''' | ||
+ | * divideMatrixByScalar(mat, scalar) - '''每個元素都除以 scalar''' | ||
+ | * multiplyMatrixByMatrix(matOne, matTwo) - '''矩陣乘法,結果亦為矩陣''' | ||
+ | * multiplyMatrixByVector(mat, vec) - '''將矩陣乘以向量,通常用來作向量的旋轉之類用途,結果亦為向量(其實是 4x4 矩陣與 4x1 矩陣相乘)''' | ||
+ | * addMatrices(matOne, matTwo) - '''兩矩陣相對應元素相加''' | ||
+ | * subtractMatrices(matOne, matTwo) - '''兩矩陣相對應元素相減''' | ||
+ | |||
+ | === Quaternion 四元數 === | ||
+ | 四元數顧名思義就是四個元素的數,請參考 [http://en.wikipedia.org/wiki/Quaternion 四元數] | ||
+ | |||
+ | * isValidQuat(quat) - '''傳回是否為有效的四元數''' | ||
+ | * makeQuat(newW, newX, newY, newZ) - '''傳回四元數 Quat = W + X * i + Y * j + Z * k, 其中 i, j, k 是虛部''' | ||
+ | * quatToMatrix(quat) - '''將四元數以矩陣來表示,其轉換如下:''' | ||
+ | <pre> | ||
+ | + ------------ ---------- ---------- --- + | ||
+ | 1 - 2(YY+ZZ) 2(XY+WZ) 2(XZ-WY) 0 | ||
+ | 2(XY-WZ) 1-2(XX+ZZ) 2(YZ+WX) 0 | ||
+ | 2(XZ+WY) 2(YZ-WX) 1-2(XX+YY) 0 | ||
+ | 0 0 0 1 | ||
+ | + ------------ ---------- ---------- --- + | ||
+ | </pre> | ||
+ | * quatToAxisAngle(axisVec, angleScalar) - '''用來將四元素的旋轉變成軸角的旋轉,不過似乎有 bug''' | ||
+ | * axisAngleToQuat(axisVec, angleScalar) - '''轉換軸角的旋轉為四元數的旋轉''' | ||
+ | * matrixToQuat(newMat) - '''將矩陣轉為四元數''' | ||
+ | * quatLengthSq(quat) - '''四元數的長度平方,等於 XX+YY+ZZ''' | ||
+ | * quatLength(quat) - '''四元數的長度 sqrt(XX+YY+ZZ)''' | ||
+ | * addQuats(quatOne, quatTwo) - '''兩個四元數相加''' | ||
+ | * subtractQuats(quatOne, quatTwo) - '''兩個四元數相減''' | ||
+ | * multiplyQuatByScalar(quatOne, scalar) - '''兩個四元數相減''' | ||
+ | * getQuatConjugate(quat) - '''四元數的共軛四元數(虛部分別為其負值)''' | ||
+ | * quatDotProduct(quatOne, quatTwo) - '''四元數的內積,其值為純量,但是並不等於 3D 意義中的長度''' | ||
+ | * normalizeQuat(quat) - '''四元數的正規化''' | ||
+ | * inverseQuat(quat) - '''反四元數''' | ||
+ | |||
+ | == Camera 相機 == | ||
+ | Camera 有分 ChaseCamera, FixedCamera, FreeCamera, PanCamera,就讓我們一個一個來看吧 | ||
+ | |||
+ | === ChaseCamera === | ||
+ | 尚未實作,空的 | ||
+ | |||
+ | === FixedCamera === | ||
+ | 嗚嗚,似乎也是未實作,看 code 似乎也沒人用到 | ||
+ | |||
+ | * 屬性 | ||
+ | ** globalPos - '''相機位置,向量''' | ||
+ | ** globalOri - '''相機方向,矩陣''' | ||
+ | ** nearClipping - '''預設值 1.0''' | ||
+ | ** farClipping - '''預設值 500''' | ||
+ | ** fieldView - '''預設值 60''' | ||
+ | ** aspectRatio - '''預設值 0''' | ||
+ | |||
+ | * 方法 | ||
+ | ** setGlobalPos(posVec) - '''未實作''' | ||
+ | ** setGlobalOri(oriMat) - '''未實作''' | ||
+ | |||
+ | === PanCamera === | ||
+ | 尚未實作,空的 | ||
+ | |||
+ | === FreeCamera === | ||
+ | |||
+ | * 方法: 讀值 | ||
+ | ** getPosition() - '''傳回位置向量''' | ||
+ | ** getUp() - '''傳回相機的 up vector(老實說我不知道這是啥或是要幹嘛)''' | ||
+ | ** getDir() - '''傳回相機瞧的方向 vector''' | ||
+ | ** getLeft() - '''傳回相機的 left vector(老實說我不知道這是啥或是要幹嘛)''' | ||
+ | ** getLinearVel() - '''註解說是 Animation of positions,其值是向量''' | ||
+ | ** getAngularVel() - '''Animations of rotation around (side Vector, up Vector, dir Vector)''' | ||
+ | |||
+ | * 方法: 設定 | ||
+ | ** setPosition(newVec) - '''設定相機位置''' | ||
+ | ** setLookAtPoint(newVec) - '''設定相機看的點,必須移(應該是沒有轉)動相機(其實是轉動所有物件)''' | ||
+ | ** setUpVector(newVec) - '''設定 up vector''' | ||
+ | ** setLinearVel(newVec) - '''設定相機的旋轉速度?,其值是向量''' | ||
+ | ** setAngularVel(newVec) - '''設定相機轉動軸?''' | ||
+ | |||
+ | * 其他功能 | ||
+ | ** rotateOnAxis(axis, angle) - '''讓相機沿著某個向量軸 axis 繞行 angle 角度''' | ||
+ | ** yaw(angle) - '''讓相機沿著其 Up vector 轉動 angle 角度''' | ||
+ | ** roll(angle) - '''讓相機沿著其方向向量轉動 angle 角度''' | ||
+ | ** pitch(angle) - '''讓相機沿著其 left vector 轉動 angle 角度''' | ||
+ | ** update(timeElapsed) - '''這個函數跟設定旋轉速度有關,我猜是供內部呼叫''' | ||
+ | ** applyToWorld(glCanvas3D, scene) - '''應該是「畫」出來的意思''' | ||
+ | |||
+ | == 畫布 Scene == | ||
+ | * 方法: 讀取 | ||
+ | ** getCamera() | ||
+ | ** getObjListSize() | ||
+ | ** getGL() - '''傳回 Canvas3D 引擎 | ||
+ | ** getTotalFrameCount() | ||
+ | ** getRenderer() | ||
+ | ** getTextureManager() | ||
+ | ** getScene() | ||
+ | ** getSkyModel() | ||
+ | ** getAmbientLight() - '''內部有個 ambientLight 陣列變數均為浮點數,假設為 a, 傳回值卻是 [a[0]/5, a[1]/5, a[2]/5, a[3]] | ||
+ | ** getObj(indxNum) - '''傳回 Scene 中的物件''' | ||
+ | * 方法: 設定 | ||
+ | ** setKeyboardCallback(keyUpCB, keyDownCB) - '''註冊鍵盤按鍵事件的聆聽函式''' | ||
+ | ** setMouseCallback(mouseUpCB, mouseDownCB, mouseMoveCB, mouseScrollCB) - '''註冊滑鼠事件的聆聽函式''' | ||
+ | ** setSkyModel(sky) | ||
+ | ** setUpdateCallback(updateCB) - '''每次 Scene 重刷時叫用''' | ||
+ | ** setRenderer(renderType) | ||
+ | ** setCanvasTag(canvasTag) | ||
+ | ** setCamera(cam) | ||
+ | ** addFloatingText(text, fontStyle, fontColour, backgroundColour) | ||
+ | ** addTextToModel(model, text, fontStyle, fontColour, backgroundColour) | ||
+ | ** create2Dcanvas(width, height) - '''Create a 2D canvas for drawing text and other stuff''' | ||
+ | ** setBackgroundColor(bgColor) | ||
+ | ** setAmbientLight(light) | ||
+ | ** init(name) | ||
+ | ** addObjectToScene(obj) | ||
+ | ** removeObjectFromScene(obj) | ||
+ | ** startScene() | ||
+ | ** render() | ||
+ | ** updateObjects(timeElapsed, camera) | ||
+ | ** renderObjects() | ||
+ | ** stopScene() | ||
+ | ** preloadImages(imagePaths) | ||
+ | |||
+ | == 虛擬世界的物件 == | ||
+ | |||
+ | === 物體 === | ||
+ | |||
+ | ==== 基本物件 Primitive ==== | ||
+ | |||
+ | * 基本屬性 | ||
+ | ** visible: true - '''物件是否可見''' | ||
+ | ** textureName - '''紋理,譬如木頭?鐵?''' | ||
+ | ** name - '''每個物件都有自己的名字''' | ||
+ | |||
+ | * 原始位置,所有物件含相機都有 left, up, dir, pos 等資訊 | ||
+ | ** left: (1, 0, 0) - '''left vector''' | ||
+ | ** up: (0, 1, 0) - '''up vector''' | ||
+ | ** dir: (0, 0, 1) - '''forward vector''' | ||
+ | ** pos: (0, 0, 0) - '''位置''' | ||
+ | ** scaleVec: (1, 1, 1) - '''放大率,預設就是 1''' | ||
+ | |||
+ | * 移動資訊 | ||
+ | ** linVel: (0, 0, 0) - '''移動"速度"為 0, 預設是靜止''' | ||
+ | ** angVel: (0, 0, 0) - '''轉動方向為 0, 預設是不轉動''' | ||
+ | |||
+ | * 取值 | ||
+ | ** getPosition() - '''傳回 pos 值''' | ||
+ | ** getUp() - '''傳回 up vector''' | ||
+ | ** getDirection() - '''傳回 dir vector''' | ||
+ | ** getLeft() - '''傳回 left vector''' | ||
+ | ** getLinearVel() - '''傳回速度 linVel 向量值''' | ||
+ | ** getAngularVel() - '''傳迴旋轉向量 angVel''' | ||
+ | ** isVisible() - '''傳回是否可見''' | ||
+ | ** getScale() - '''傳回放大率''' | ||
+ | ** getName() - '''傳回物件名稱''' | ||
+ | ** getTextureName() - '''傳迴紋理名稱''' | ||
+ | |||
+ | * 設定 | ||
+ | ** setTexture(imageFilename) - '''設定紋理檔案名稱''' | ||
+ | ** setTextureFromCanvas2D(sourceCanvas) - '''從 Canvas2D 設定紋理''' | ||
+ | ** unsetTexture() - '''重設紋理為空的''' | ||
+ | ** setName(name) - '''設定物件名稱''' | ||
+ | ** setVisible(show) - '''設定物件是否可見''' | ||
+ | ** scale(scaleVec) - '''設定物件放大比例''' | ||
+ | ** setPosition(vecPos) - '''設定物件位置 pos 值''' | ||
+ | ** translate(translation) - '''將物件放到新位置(相當於 pos+translation)''' | ||
+ | ** setForward(newVec) - '''將物件往 newVec 方向移動,這會影響到 up, left 等向量''' | ||
+ | ** setUpVector(newVec) - '''設定 up vector 成 newVec 值''' | ||
+ | ** setLinearVel(newVec) - '''設定速度值為 newVec 值''' | ||
+ | ** setAngularVel(newVec) - '''設定旋轉方向為 newVec 值''' | ||
+ | ** rotateOnAxis(axisVec, angle) - '''以四元數及矩陣來計算物件的旋轉, 先移動 dir, 再計算相對應的 left, up 值''' | ||
+ | ** yaw(angle) - '''效果是 rotateOnAxis(up, angle)''' | ||
+ | ** roll(angle) - '''效果是 rotateOnAxis(dir, angle)''' | ||
+ | ** pitch(angle) - '''效果是 rotateOnAxis(left, angle)''' | ||
+ | ** update(timeStep) - '''依照速度向量 linVel 移動物體的 pos 位置向量,並調整 up, dir, left 等向量值''' | ||
+ | ** render(glCanvas3D) - '''透過 glCanvas3D 這個 plugin 把物件畫出來,這邊會有顏色,只是怪怪的''' | ||
+ | |||
+ | ==== Model (繼承自 Primitive) ==== | ||
+ | * 屬性 | ||
+ | ** loadedTexture: false - '''是否已載入紋理''' | ||
+ | ** expandedVertices - '''似乎要用來放大縮小用的''' | ||
+ | ** expandedNormals | ||
+ | ** expandedUVs | ||
+ | ** buffers | ||
+ | ** firstTimeRender: true - '''第一次畫出來''' | ||
+ | ** stayInFrontOfCamera: false - '''是否在相機前,應該是與是否可見有關''' | ||
+ | * 方法 | ||
+ | ** init(vertices, normals, texcoords, faces) - '''必要參數是 vertices, faces。normal是單位大小,或許與燈光有關,texcoords 是紋理座標(不懂),而 faces 則是引索陣列,引用 texcoords 來當其表面紋理''' | ||
+ | ** initDAE(path) - '''dae是Collada的檔案副檔名,是用xml去儲存3d的模型''' | ||
+ | ** createBuffers(glCanvas3D) - '''應該是在根據 expandedVertices 準備畫布,若有設定 expandedUVs, expandedNormals, 也會準備相對應的 Buffer''' | ||
+ | ** update(timeStep, camera) - '''會先根據相機移動相對位置後,再根據自己的速度與旋轉來移動自己''' | ||
+ | ** render(glCanvas3D, scene) - '''要把物體畫出來,相對上麻煩的多''' | ||
+ | |||
+ | ==== 立方體 Cube ==== | ||
+ | 繼承自基本物件 Primitive | ||
+ | |||
+ | ===== 轉換矩陣 ===== | ||
+ | 為了效率考量,定義了幾個轉換用的矩陣: | ||
+ | |||
+ | * cube_transition_Vertices = | ||
+ | <pre> | ||
+ | [ | ||
+ | [-1, -1, 1], // 0 - front, bottom, left | ||
+ | [-1, 1, 1], // 1 - front, top, left | ||
+ | [ 1, 1, 1], // 2 - front, top, right | ||
+ | [ 1, -1, 1], // 3 - front, bottom, right | ||
+ | |||
+ | [-1, -1, -1], // 4 - back, bottom, left | ||
+ | [-1, 1, -1], // 5 - back, top, left | ||
+ | [ 1, 1, -1], // 6 - back, top, right | ||
+ | [ 1, -1, -1] // 7 - back, bottom, right | ||
+ | ]; | ||
+ | </pre> | ||
+ | * cube_transition_Normals = | ||
+ | <pre> | ||
+ | [ | ||
+ | [-0.57735,-0.57735, 0.57735], // front, bottom, left | ||
+ | [-0.57735, 0.57735, 0.57735], // front, top, left | ||
+ | [ 0.57735, 0.57735, 0.57735], // front, top, right | ||
+ | [ 0.57735,-0.57735, 0.57735], // front, bottom, right | ||
+ | |||
+ | [-0.57735,-0.57735, -0.57735], // back, bottom, left | ||
+ | [-0.57735, 0.57735, -0.57735], // back, top, left | ||
+ | [ 0.57735, 0.57735, -0.57735], // back, top, right | ||
+ | [ 0.57735,-0.57735, -0.57735] // back, bottom, right | ||
+ | ]; | ||
+ | </pre> | ||
+ | * cube_transition_UVs = | ||
+ | <pre> | ||
+ | [ | ||
+ | [0.0,1.0], // 0 - bottom left | ||
+ | [0.0,0.0], // 1 - top left | ||
+ | [1.0,0.0], // 2 - top right | ||
+ | [1.0,1.0] // 3 - bottom right | ||
+ | ]; | ||
+ | </pre> | ||
+ | * cube_transition_Faces = | ||
+ | <pre> | ||
+ | [ | ||
+ | [0,0,0], [3,3,3], [2,2,2], // front | ||
+ | [0,0,0], [2,2,2], [1,1,1], | ||
+ | |||
+ | [5,2,5], [6,1,6], [7,0,7], // back | ||
+ | [5,2,5], [7,0,7], [4,3,4], | ||
+ | |||
+ | [4,0,4], [7,3,7], [3,2,3], // bottom | ||
+ | [4,0,4], [3,2,3], [0,1,0], | ||
+ | |||
+ | [1,0,1], [2,3,2], [6,2,6], // top | ||
+ | [1,0,1], [6,2,6], [5,1,5], | ||
+ | |||
+ | [4,0,4], [0,3,0], [1,2,1], // left side | ||
+ | [4,0,4], [1,2,1], [5,1,5], | ||
+ | |||
+ | [3,0,3], [7,3,7], [6,2,6], // right side | ||
+ | [3,0,3], [6,2,6], [2,1,2] | ||
+ | ]; | ||
+ | </pre> | ||
+ | |||
+ | ===== 屬性與方法 ===== | ||
+ | |||
+ | * 屬性 | ||
+ | ** m - '''就是 Model 物件''' | ||
+ | ** 其餘均繼承自 Model | ||
+ | |||
+ | * 方法 - 除 scale 外,全部繼承自 Model | ||
+ | ** init(), isVisible() | ||
+ | ** getPosition(), getUp(), getDirection(), getLeft(), getLinearVel(), getAngularVel(), getScale(), getTextureName(), | ||
+ | ** setTexture(imageFilename), setTextureFromCanvas2D(sourceCanvas), unsetTexture(), setVisible(show) | ||
+ | ** setPosition(vecPos), setForward(newVec), setUpVector(newVec), | ||
+ | ** translate(translation), setLinearVel(newVec), setAngularVel(newVec), rotateOnAxis(axisVec, angle) | ||
+ | ** yaw(angle), roll(angle), pitch(angle), update(timeStep), render(glCanvas3D, scene) | ||
+ | ** scale(scaleVec, scaleY, scaleZ) - '''多了 Y, Z 二個軸向的放大縮小?忽略的話就跟 Model 一樣''' | ||
+ | |||
+ | === Primitive Class === | ||
+ | === Model Class === |
於 2008年10月9日 (四) 17:25 的最新修訂
簡介
Canvas 3D JS Library (C3DL) 是 javascript 函式庫,也就是裡頭提供的全部是 Javascript, 必須安裝 Canvas3D extension of firefox, c3dl 程式碼在此。主要目的是讓你在 Firefox/Mozilla 平台用 Canvas/OpenGL 的方式撰寫 3D 的網路應用。
C3DL 提供一系列的數學、景觀、及3D物件類別,讓你在用 Canvas 會更有彈性,當然主要就是要縮短開發時間。
本專案開發人員
- Catherine Leung
- Mark Paruzel (CodeBot)
- Andrew Smith
- Chris Bishop (Javascript)
- Andor Salga
有用的連結
- 我們的網站
- 我們的 blog
- Our SVN Repo: svn://cdot.senecac.on.ca/canvas3d
- 在 Vlad's hg repo 中的 canvas3d 源碼
- OpenGL 文件
- Nehe 的 OpenGL 教學
- .off (object file format) format info
類別繼承圖
數學運算
我的感覺是這份 c3dl 並不是非常有效率,可以稍微修正寫法,譬如參考 paperVision3D
Vector 向量類別
一個向量基本上就是在 3D 世界的 X, Y, Z 三個軸的座標系統描述一個「具備大小的方向」。3D 數學存在各種不同的座標系統,離開向量類別的封裝則不復存在所謂的 3D。向量類別具有下列的成員;
- isValidVector(vecArr) - 判斷參數是否為一有效的向量
- copyVector(srcVec) - 從 srcVec 複製並傳回
- copyVectorContents(srcVec, destVec) - 效果相當於(不等於,因為有多作判斷是否為有效向量) destVec = copyVector(srcVec);
- makeVector(newX, newY, newZ) - copyVector() 就是用這個函數實作向量複製
- normalizeVector(vec) - 計算與向量相同方向但長度為一的「單位向量」並傳回
- vectorDotProduct(vecOne, vecTwo) - 傳回兩向量的內積(點積),其值為純量
- vectorCrossProduct(vecOne, vecTwo, dest) - 將 vecOne 與 vecTwo 兩個向量做外積(叉積)後指定給 dest)
- vectorLength(vec) - 計算並傳回向量的長度(相當於與自己的內積開根號)
- vectorLengthSq(vec) - 計算向量長度的平方,相比於直接利用長度的運算上,少了一個根號後再平方的無用計算
- addVectors(vecOne, vecTwo, dest) - 相當於 dest = vecOne + vecTwo
- subtractVectors(vecOne, vecTwo, dest) - 相當於 dest = vecOne - vecTwo
- multiplyVector(vec, scalar, dest) - 相當於 dest = vec * scalar; 效果相當於將向量放大(scalar 小於1的正數則為縮小,負數則為反向)
- divideVector(vec, scalar, dest) - 相當於 dest = vec / scalar; 效果相當於將向量縮小(scalar 小於1的正數則為放大,負數則為反向)
- multiplyVectorByVector(vecOne, vecTwo, dest) - 既非內積也非外積,而是相當於 dest = [X1*X2, Y1*Y2, Z1*Z2]; 的乘法
- isVectorEqual(vecOne, vecTwo) - 判斷兩向量是否相等
- isVectorZero(vec) - 判斷向量長度是否為 0,正確的說法: 判斷是否極接近 0,每個軸向誤差在 0.00001 以內
- getAngleBetweenVectors(vecOne, vecTwo) - 計算兩向量間的夾角
Matrix 矩陣類別
c3dl 的矩陣在數學上是一個 4x4 的二維矩陣,但是在 Javascript 實作上是用一維陣列來表達,而不是二維陣列,其索引值如下:
+- -+ | 0, 4, 8, 12 | | 1, 5, 9, 13 | | 2, 6, 10, 14 | | 3, 7, 11, 15 | +- -+
- isValidMatrix(mat) - 判斷是否為有效的矩陣
- makeIdentityMatrix() - 產生單位矩陣,也就是斜對角都為 1 其餘為 0 的矩陣
- makeZeroMatrix() - 產生全部是0 的矩陣
- makeMatrix(e00, e01, e02, e03, e10, e11, e12, e13, e20, e21, e22, e23, e30, e31, e32, e33) - 利用參數產生矩陣,其索引值順序如上述,或由此處的宣告亦可得知
- matricesEqual(matrix1, matrix2) - 判斷兩矩陣是否相等
- makePoseMatrix(vecLeft, vecUp, vecFrwd, vecPos) - 位置矩陣,通常用來處理「手勢」,效果如下:
+- -+ | Left.x, Up.x, Fwd.x, Pos.x | | Left.y, Up.y, Fwd.y, Pos.y | | Left.z, Up.z, Fwd.z, Pos.z | | 0.0, 0.0, 0.0, 1.0 | +- -+
- transposeMatrix(mat) - 傳回轉置矩陣: 型如
+- -+ | A, B, C, D | | E, F, G, H | | I, J, K, L | | M, N, O, P | +- -+
轉置結果為:
+- -+ | A, E, I, M | | B, F, J, N | | C, G, K, O | | D, H, L, P | +- -+
- inverseMatrix(mat) - 計算並傳回 mat 的反矩陣,使得 res * mat = I, 其中 I 是單位矩陣,參考 反矩陣
- matrixDeterminant(mat) - 計算並傳回 mat 的行列式,通常用來算面積或體積,其值為純量,參考 行列式
- matrixAdjoint(mat) - 不知道怎麼翻,伴隨矩陣?共軛矩陣?參考 adjoint matrix
- multiplyMatrixByScalar(mat, scalar) - 每個元素都乘以 scalar
- divideMatrixByScalar(mat, scalar) - 每個元素都除以 scalar
- multiplyMatrixByMatrix(matOne, matTwo) - 矩陣乘法,結果亦為矩陣
- multiplyMatrixByVector(mat, vec) - 將矩陣乘以向量,通常用來作向量的旋轉之類用途,結果亦為向量(其實是 4x4 矩陣與 4x1 矩陣相乘)
- addMatrices(matOne, matTwo) - 兩矩陣相對應元素相加
- subtractMatrices(matOne, matTwo) - 兩矩陣相對應元素相減
Quaternion 四元數
四元數顧名思義就是四個元素的數,請參考 四元數
- isValidQuat(quat) - 傳回是否為有效的四元數
- makeQuat(newW, newX, newY, newZ) - 傳回四元數 Quat = W + X * i + Y * j + Z * k, 其中 i, j, k 是虛部
- quatToMatrix(quat) - 將四元數以矩陣來表示,其轉換如下:
+ ------------ ---------- ---------- --- + 1 - 2(YY+ZZ) 2(XY+WZ) 2(XZ-WY) 0 2(XY-WZ) 1-2(XX+ZZ) 2(YZ+WX) 0 2(XZ+WY) 2(YZ-WX) 1-2(XX+YY) 0 0 0 0 1 + ------------ ---------- ---------- --- +
- quatToAxisAngle(axisVec, angleScalar) - 用來將四元素的旋轉變成軸角的旋轉,不過似乎有 bug
- axisAngleToQuat(axisVec, angleScalar) - 轉換軸角的旋轉為四元數的旋轉
- matrixToQuat(newMat) - 將矩陣轉為四元數
- quatLengthSq(quat) - 四元數的長度平方,等於 XX+YY+ZZ
- quatLength(quat) - 四元數的長度 sqrt(XX+YY+ZZ)
- addQuats(quatOne, quatTwo) - 兩個四元數相加
- subtractQuats(quatOne, quatTwo) - 兩個四元數相減
- multiplyQuatByScalar(quatOne, scalar) - 兩個四元數相減
- getQuatConjugate(quat) - 四元數的共軛四元數(虛部分別為其負值)
- quatDotProduct(quatOne, quatTwo) - 四元數的內積,其值為純量,但是並不等於 3D 意義中的長度
- normalizeQuat(quat) - 四元數的正規化
- inverseQuat(quat) - 反四元數
Camera 相機
Camera 有分 ChaseCamera, FixedCamera, FreeCamera, PanCamera,就讓我們一個一個來看吧
ChaseCamera
尚未實作,空的
FixedCamera
嗚嗚,似乎也是未實作,看 code 似乎也沒人用到
- 屬性
- globalPos - 相機位置,向量
- globalOri - 相機方向,矩陣
- nearClipping - 預設值 1.0
- farClipping - 預設值 500
- fieldView - 預設值 60
- aspectRatio - 預設值 0
- 方法
- setGlobalPos(posVec) - 未實作
- setGlobalOri(oriMat) - 未實作
PanCamera
尚未實作,空的
FreeCamera
- 方法: 讀值
- getPosition() - 傳回位置向量
- getUp() - 傳回相機的 up vector(老實說我不知道這是啥或是要幹嘛)
- getDir() - 傳回相機瞧的方向 vector
- getLeft() - 傳回相機的 left vector(老實說我不知道這是啥或是要幹嘛)
- getLinearVel() - 註解說是 Animation of positions,其值是向量
- getAngularVel() - Animations of rotation around (side Vector, up Vector, dir Vector)
- 方法: 設定
- setPosition(newVec) - 設定相機位置
- setLookAtPoint(newVec) - 設定相機看的點,必須移(應該是沒有轉)動相機(其實是轉動所有物件)
- setUpVector(newVec) - 設定 up vector
- setLinearVel(newVec) - 設定相機的旋轉速度?,其值是向量
- setAngularVel(newVec) - 設定相機轉動軸?
- 其他功能
- rotateOnAxis(axis, angle) - 讓相機沿著某個向量軸 axis 繞行 angle 角度
- yaw(angle) - 讓相機沿著其 Up vector 轉動 angle 角度
- roll(angle) - 讓相機沿著其方向向量轉動 angle 角度
- pitch(angle) - 讓相機沿著其 left vector 轉動 angle 角度
- update(timeElapsed) - 這個函數跟設定旋轉速度有關,我猜是供內部呼叫
- applyToWorld(glCanvas3D, scene) - 應該是「畫」出來的意思
畫布 Scene
- 方法: 讀取
- getCamera()
- getObjListSize()
- getGL() - 傳回 Canvas3D 引擎
- getTotalFrameCount()
- getRenderer()
- getTextureManager()
- getScene()
- getSkyModel()
- getAmbientLight() - 內部有個 ambientLight 陣列變數均為浮點數,假設為 a, 傳回值卻是 [a[0]/5, a[1]/5, a[2]/5, a[3]]
- getObj(indxNum) - 傳回 Scene 中的物件
- 方法: 設定
- setKeyboardCallback(keyUpCB, keyDownCB) - 註冊鍵盤按鍵事件的聆聽函式
- setMouseCallback(mouseUpCB, mouseDownCB, mouseMoveCB, mouseScrollCB) - 註冊滑鼠事件的聆聽函式
- setSkyModel(sky)
- setUpdateCallback(updateCB) - 每次 Scene 重刷時叫用
- setRenderer(renderType)
- setCanvasTag(canvasTag)
- setCamera(cam)
- addFloatingText(text, fontStyle, fontColour, backgroundColour)
- addTextToModel(model, text, fontStyle, fontColour, backgroundColour)
- create2Dcanvas(width, height) - Create a 2D canvas for drawing text and other stuff
- setBackgroundColor(bgColor)
- setAmbientLight(light)
- init(name)
- addObjectToScene(obj)
- removeObjectFromScene(obj)
- startScene()
- render()
- updateObjects(timeElapsed, camera)
- renderObjects()
- stopScene()
- preloadImages(imagePaths)
虛擬世界的物件
物體
基本物件 Primitive
- 基本屬性
- visible: true - 物件是否可見
- textureName - 紋理,譬如木頭?鐵?
- name - 每個物件都有自己的名字
- 原始位置,所有物件含相機都有 left, up, dir, pos 等資訊
- left: (1, 0, 0) - left vector
- up: (0, 1, 0) - up vector
- dir: (0, 0, 1) - forward vector
- pos: (0, 0, 0) - 位置
- scaleVec: (1, 1, 1) - 放大率,預設就是 1
- 移動資訊
- linVel: (0, 0, 0) - 移動"速度"為 0, 預設是靜止
- angVel: (0, 0, 0) - 轉動方向為 0, 預設是不轉動
- 取值
- getPosition() - 傳回 pos 值
- getUp() - 傳回 up vector
- getDirection() - 傳回 dir vector
- getLeft() - 傳回 left vector
- getLinearVel() - 傳回速度 linVel 向量值
- getAngularVel() - 傳迴旋轉向量 angVel
- isVisible() - 傳回是否可見
- getScale() - 傳回放大率
- getName() - 傳回物件名稱
- getTextureName() - 傳迴紋理名稱
- 設定
- setTexture(imageFilename) - 設定紋理檔案名稱
- setTextureFromCanvas2D(sourceCanvas) - 從 Canvas2D 設定紋理
- unsetTexture() - 重設紋理為空的
- setName(name) - 設定物件名稱
- setVisible(show) - 設定物件是否可見
- scale(scaleVec) - 設定物件放大比例
- setPosition(vecPos) - 設定物件位置 pos 值
- translate(translation) - 將物件放到新位置(相當於 pos+translation)
- setForward(newVec) - 將物件往 newVec 方向移動,這會影響到 up, left 等向量
- setUpVector(newVec) - 設定 up vector 成 newVec 值
- setLinearVel(newVec) - 設定速度值為 newVec 值
- setAngularVel(newVec) - 設定旋轉方向為 newVec 值
- rotateOnAxis(axisVec, angle) - 以四元數及矩陣來計算物件的旋轉, 先移動 dir, 再計算相對應的 left, up 值
- yaw(angle) - 效果是 rotateOnAxis(up, angle)
- roll(angle) - 效果是 rotateOnAxis(dir, angle)
- pitch(angle) - 效果是 rotateOnAxis(left, angle)
- update(timeStep) - 依照速度向量 linVel 移動物體的 pos 位置向量,並調整 up, dir, left 等向量值
- render(glCanvas3D) - 透過 glCanvas3D 這個 plugin 把物件畫出來,這邊會有顏色,只是怪怪的
Model (繼承自 Primitive)
- 屬性
- loadedTexture: false - 是否已載入紋理
- expandedVertices - 似乎要用來放大縮小用的
- expandedNormals
- expandedUVs
- buffers
- firstTimeRender: true - 第一次畫出來
- stayInFrontOfCamera: false - 是否在相機前,應該是與是否可見有關
- 方法
- init(vertices, normals, texcoords, faces) - 必要參數是 vertices, faces。normal是單位大小,或許與燈光有關,texcoords 是紋理座標(不懂),而 faces 則是引索陣列,引用 texcoords 來當其表面紋理
- initDAE(path) - dae是Collada的檔案副檔名,是用xml去儲存3d的模型
- createBuffers(glCanvas3D) - 應該是在根據 expandedVertices 準備畫布,若有設定 expandedUVs, expandedNormals, 也會準備相對應的 Buffer
- update(timeStep, camera) - 會先根據相機移動相對位置後,再根據自己的速度與旋轉來移動自己
- render(glCanvas3D, scene) - 要把物體畫出來,相對上麻煩的多
立方體 Cube
繼承自基本物件 Primitive
轉換矩陣
為了效率考量,定義了幾個轉換用的矩陣:
- cube_transition_Vertices =
[ [-1, -1, 1], // 0 - front, bottom, left [-1, 1, 1], // 1 - front, top, left [ 1, 1, 1], // 2 - front, top, right [ 1, -1, 1], // 3 - front, bottom, right [-1, -1, -1], // 4 - back, bottom, left [-1, 1, -1], // 5 - back, top, left [ 1, 1, -1], // 6 - back, top, right [ 1, -1, -1] // 7 - back, bottom, right ];
- cube_transition_Normals =
[ [-0.57735,-0.57735, 0.57735], // front, bottom, left [-0.57735, 0.57735, 0.57735], // front, top, left [ 0.57735, 0.57735, 0.57735], // front, top, right [ 0.57735,-0.57735, 0.57735], // front, bottom, right [-0.57735,-0.57735, -0.57735], // back, bottom, left [-0.57735, 0.57735, -0.57735], // back, top, left [ 0.57735, 0.57735, -0.57735], // back, top, right [ 0.57735,-0.57735, -0.57735] // back, bottom, right ];
- cube_transition_UVs =
[ [0.0,1.0], // 0 - bottom left [0.0,0.0], // 1 - top left [1.0,0.0], // 2 - top right [1.0,1.0] // 3 - bottom right ];
- cube_transition_Faces =
[ [0,0,0], [3,3,3], [2,2,2], // front [0,0,0], [2,2,2], [1,1,1], [5,2,5], [6,1,6], [7,0,7], // back [5,2,5], [7,0,7], [4,3,4], [4,0,4], [7,3,7], [3,2,3], // bottom [4,0,4], [3,2,3], [0,1,0], [1,0,1], [2,3,2], [6,2,6], // top [1,0,1], [6,2,6], [5,1,5], [4,0,4], [0,3,0], [1,2,1], // left side [4,0,4], [1,2,1], [5,1,5], [3,0,3], [7,3,7], [6,2,6], // right side [3,0,3], [6,2,6], [2,1,2] ];
屬性與方法
- 屬性
- m - 就是 Model 物件
- 其餘均繼承自 Model
- 方法 - 除 scale 外,全部繼承自 Model
- init(), isVisible()
- getPosition(), getUp(), getDirection(), getLeft(), getLinearVel(), getAngularVel(), getScale(), getTextureName(),
- setTexture(imageFilename), setTextureFromCanvas2D(sourceCanvas), unsetTexture(), setVisible(show)
- setPosition(vecPos), setForward(newVec), setUpVector(newVec),
- translate(translation), setLinearVel(newVec), setAngularVel(newVec), rotateOnAxis(axisVec, angle)
- yaw(angle), roll(angle), pitch(angle), update(timeStep), render(glCanvas3D, scene)
- scale(scaleVec, scaleY, scaleZ) - 多了 Y, Z 二個軸向的放大縮小?忽略的話就跟 Model 一樣