LearingWebGL Lesson one WebGL教程

歡迎來到我的第一個WebGL教程!這節教程是以NeHe的OpenGL教程第二課爲基礎的(譯者注:這裏有網上找到的NeHe的OpenGL中文教程),NeHe的OpenGL教程是學習3D圖形遊戲編程很流行的一個教程。這節課向你解釋怎樣在頁面上繪製一個三邊形和一個四邊形。也許這本身不是那麼令人興奮,但這是對WebGL基礎的一個很好的介紹:如果你知道它是這樣工作,剩下的會是十分相似……
這兒是這節課的代碼在支持WebGL的瀏覽器運行看起來的效果:






一個小的忠告:這些教程所針對的人羣是具有一定的編程知識,但是沒有真正的3D圖形編程經驗;目的是使你入門並且知道這些代碼是怎樣運行的,以至於你能夠儘快開始製作自己的3D網頁。我自己寫這些代碼是因爲我在自學WebGL,所以可能有錯誤;你自己負責是否使用它。然而我一直在校準bugs和改正錯誤的地方當我聽到相關信息的時候,所以如果你看到哪些地方有錯誤,請在下面的留言裏面讓我知道。
有兩種方法你能夠得到這個示例的代碼:當你看現場演示的時候,查看源代碼或是如果你使用GitHub,你能夠複製它(還有以後的教程)從那個代碼庫。兩者選一,一旦你有了代碼,加載到你最喜歡的文本編輯器裏看一看。看第一眼是非常讓人畏懼的,儘管你對OpenGL有一定的熟悉。對了,在開始,我們定義了一對渲染器,渲染器一般被認爲是相當高級的……但是不要絕望,它實際上比看起來簡單多了。
就像大部分的編程,這個WebGL頁面是通過在頁面底部定義一系列被高層次代碼所使用的低層次函數開始運行的。爲了解釋它,我將從頁面底部開始講解並且逐漸向上面講,所以如果你想讓代碼跟上來,跳到頁面的底部。
你將看到下面的HTML的代碼:
< body οnlοad="webGLStart();">
<a href="http://learningwebgl.com/blog/?p=28">&lt;&lt; Back to Lesson 1</a><br />
<canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>
<br/>
<a href="http://learningwebgl.com/blog/?p=28">&lt;&lt; Back to Lesson 1</a><br />
</body>
這完全是一個網頁的body部分——其餘的部分都是JavaScript(儘管如果你通過“查看源代碼”獲得代碼,你將獲得一些其他的被我的服務器解析的代碼,你可以忽略。)顯然地我們能夠放置很多標準的HTML標籤放在 <body>標籤之間來將WebGL圖像構建爲一個標準的網頁,但是對於這個簡單的頁面我們僅僅獲得WebGL並且設置指向這個博客的鏈接,這個 <canvas>標籤是用來放置3D圖形。Canvas是HTML5的一個新特性——它支持JavaScript在網頁上繪製2D和(通過WebGL)3D。我們不需要指明其它信息除了在canvas標籤裏指明簡單的佈局屬性,而是將WebGL設置代碼留到一個簡單的名爲webglStart的JavaScript函數上,一旦頁面被加載,這個函數就被調用。
現在讓我們向上滾動到函數的地方來看一下:
  function webGLStart() {
    var canvas = document.getElementById("lesson01-canvas");
      initGL(canvas);
      initShaders();
      initBuffers();
       gl.clearColor(0.0, 0.0, 0.0, 1.0);
      gl.enable(gl.DEPTH_TEST);
      drawScene();
    }
通過調用函數來初始化WebGL和我前面提到的渲染器,傳入以前的我們想要繪製3D圖形的canvas元素,再使用initBuffers初始化一些緩存;緩存記錄了我們將要繪製的三邊形和四邊形的詳細信息——過一會兒我們將會詳細談論它們。接下來,是一些基本的GL引擎設置,當我們清除畫面的時候我們會將畫面設置爲黑色,100%的清除我們將展示的圖形,並且我們需要進行深度測試(以至於繪製的後面物體要被前面的物體遮擋)。這些步驟是通過調用gl對象的方法來完成的——後面我們將會看到是怎麼初始化的。最後,調用drawScene函數;這個函數(正如你根據它的名字推測的)使用緩存繪製一個三角形和一個四邊形。
稍後,我們再來看看initGL和initShader這兩個函數,因爲它們對於理解頁面怎樣工作很重要,但是,首先,讓我們來看看initBuffers和drawScene這兩個函數吧。
首先看看initBuffers;一行一行分析:
    var triangleVertexPositionBuffer;
    var squareVertexPositionBuffer;
我們聲明兩個變量存儲緩存。(在真正的WebGL頁面,你不需要爲屏幕中的每一個對象分配不同的變量,但是在第一課我們這樣做是爲了讓事情變得簡單點)
接下來:
    function initBuffers() {
     triangleVertexPositionBuffer = gl.createBuffer();
我們爲三邊形的位置創建一個緩存。頂點(你不喜歡有規律的複數嗎?)在三維空間裏面確定我們要繪製圖形的點。對於我們的三邊形,我們將會有三個點(等下我們會設置)。這個緩存實際上只佔用一點點顯存;在初始化代碼中通過將頂點位置寫入顯卡,然後當再次繪製圖形的時候,本質上我們只是告訴WebGL“繪製先前我告訴你的圖形”,這樣我們能夠使得我們的代碼很有效率。當然,在這種情況下我們只設置了三個頂點,將它們寫入顯卡不需要太多的開銷——但是當你處理含有上萬個頂點的大模型的時候,這樣做會體現出它真正的優勢的。
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
這一行告訴WebGL接下來的操作必須在我們指定的緩存中運行。一直要有一個概念就是“當前的數組緩存”,函數是在當前的數組緩存中運行而不是你想在哪個緩存中運行就能運行的。比較奇怪,但是我確定後面的代碼能夠很好解釋原因……
    var vertices = [
         0.0, 1.0, 0.0,
        -1.0, -1.0, 0.0,
         1.0, -1.0, 0.0
    ];
下一步,我們定義我們的頂點位置作爲一個JavaScript列表。你能夠發現它們是以(0,0,0)爲中心的等腰三角形的頂點。
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
現在我們根據我們的JavaScript列表創建一個Float32Array對象,並且告訴WebGL使用它來填充當前的緩存,也就是我們的triangleVertexPositionBuffer。我們將會談論更多關於Float32Arrays的用法在以後的教程中,但對於現在所有你需要知道的是它們將JavaScript列表轉換爲一種格式,我們能夠將這種格式傳入WebGL填充緩存。
    triangleVertexPositionBuffer.itemSize = 3;
    triangleVertexPositionBuffer.numItems = 3;
最後關於緩存的設置就是給它添加兩個新屬性。這不是WebGL的一部分,但是它在後面的代碼裏面會很有用。一個關於JavaScript非常好的事情(有些人也會說,壞事情)就是一個對象不一定要明確地支持一個特殊的屬性,你可以在對象上隨便設置屬性。因此儘管buffer對象以前沒有itemSize和numItems屬性,一旦你設置,它們就有了。我們使用這兩個屬性來說明這個含九個元素的緩存實際上代表着三個分開的頂點位置(numItems),每個頂點位置又由三個數組成(itemSize)。
現在我們完全設置了三邊形的緩存,該輪到四邊形了:
    squareVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    vertices = [
         1.0, 1.0, 0.0,
        -1.0, 1.0, 0.0,
         1.0, -1.0, 0.0,
        -1.0, -1.0, 0.0
     ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    squareVertexPositionBuffer.itemSize = 3;
    squareVertexPositionBuffer.numItems = 4;
     }
上面的代碼是非常明顯的——四邊形有四個頂點位置而不是三個,所以數組更大,numItems屬性也不相同。
OK,這些是將對象的頂點位置寫入顯卡所需要做的。現在讓我們看看drawScene函數,這個函數完成了將這些緩存真正繪製成我們所見到的這個圖像。一行一行來分析:
     function drawScene() {
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
第一步是使用viewport函數告訴WebGL畫布的大小;在比較後面的課程中我們將會返回來看看爲什麼這一步是非常重要的;現在你只需要知道開始繪製之前要獲得canvas的尺寸。接下來,我們清除畫布爲在它上面繪圖做準備:
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
……然後:
    mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
這兒我們設置想要的透視效果。默認的,WebGL會將遠處近處的物體繪製成一樣大的尺寸(也就是3D的正交投影)。爲了使遠處的物體看起來更小,我們需要告訴WebGL我們使用了透視。對於這個場景,我們指定了(垂直)視野是45°;,我們告訴WebGL畫布的寬-高比,並且我們不想要看到離0.1單元近的物體,100單元遠的物體。
正如你見到的,perspective使用了mat4模塊中的一個函數,並且包含了一個名字比較有趣的變量pMatrix。稍後會談論得更多;如果順利的話,現在我們不用知道它的具體情況但可以很清楚知道它們怎麼使用。
既然我們已經設立了perspective,我們就能夠繼續繪製我們的物體了:
    mat4.identity(mvMatrix);
第一步是移動到3D場景的中心。在OpenGL裏,當你繪製一個場景的時候,你告訴它在“當前的”位置“當前的”角度繪製圖形——所以,比如,“向前移動20單元,旋轉32°,然後繪製機器人”,後面一些複雜的設置“移動多少,旋轉多少,繪製什麼”說明了自身的一些特性。這是非常有用的,因爲你能夠將繪製機器人的代碼封裝爲一個函數,然後可以很容易在某個地方繪製這個機器人通過改變移動/旋轉的數據再調用那個函數。
當前的位置和旋轉的角度儲存在一個矩陣中,正如你在學校裏可能學到的,矩陣能夠代表移動(從一個地方到另一個地方),旋轉和其它的幾何變化。由於一些原因我將不會詳細講,你能夠使用4×4矩陣(不是3×3)代表3D空間中的任一一個變化;你以單位矩陣開始——也就是說不代表任何移動——然後乘以代表第一個移動的矩陣,緊臨着乘以代表第二個移動的矩陣,等等。這個複合的矩陣就代表你所有的移動了。我們使用的代表當前移動/旋轉狀態的矩陣被叫做模型視圖矩陣,現在你可能推測出變量mvMatrix保存着我們的模型視圖矩陣,並且我們剛纔調用的mat.identity函數是將模型視圖矩陣變爲單位矩陣以至於爲後面的移動和旋轉變換做準備。或者,換一句話說,它將我們從開始繪製3D的世界移動到了原點。
眼睛敏銳的讀者將會注意在開始討論矩陣的時候我是說“在OpenGL裏”,不是在“WebGL裏”。這是因爲WebGL沒有將這些東西內建在圖形庫裏。作爲替代,我們使用了一個第三方的矩陣庫—— Brandon Jones的優秀的glMatrix——加上一些極好的能夠達到同樣效果的WebGL技巧。一些更多的相關技巧會在後面談到。
下一步是真正繪製圖形!
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
你需記住爲了使用其中的緩存,我們調用gl.bindBuffer指明當前的緩存,然後調用將在其上面運行的代碼。這裏我們選擇了triangleVertexPositionBuffer,然後告訴WebGL緩存裏面的值被用作頂點的位置。後面我將會解釋更多關於它們運行原理,現在,你能夠看到我們使用先前在緩存上設置的屬性來告訴WebGL緩存中的每一項含三個數。
下一步,我們:
    setMatrixUniforms();
告訴WebGL要考慮當前模型視圖矩陣(也包括投影矩陣,在後面將有介紹)。這是必要的因爲WebGL沒自建矩陣。你能夠通過用來移動的mvTranslate函數來查看矩陣信息,但是矩陣的運算都是在JavaScript私有域中發生的。setMatrixUniforms函數將數據傳遞給顯卡。
一旦這些設置好了之後,WebGL知道了有個數組要作爲頂點位置,而且知道我們設置的矩陣。下一步告訴它用這些數組和矩陣做什麼:
    gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);
或者換一句話,“將剛纔給你的含頂點的數組用三角形的方式繪製,從數組中的第零項開始到第numItems項。”
一旦完成,WebGL將繪製我們的三邊形。下一步,繪製四邊形:
    mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);
開始我們向右移動模型視圖矩陣3個單位。記住,我們之前已經向左移動了1.5個單位、向屏幕裏面移動了7個單位,因此我們還處於向右1.5向裏7的位置。
下一步:
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
告訴WebGL將四邊形緩存作爲頂點位置來使用……
    setMatrixUniforms();
我們再次將模型視圖矩陣和投影矩陣調出來(這與最近的mvTranslate函數有關),這意味着我們最後能夠完成:
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
繪製這些點。你也許會問,什麼是三角形帶(triangle strip)?好的,三角形帶是非常有用的,它是一串三角形,你給出的前三個點確定第一個三角形,前面三點的後面兩點加上另外確定的一點確定第二個三角形,等等。在這裏,用這種方法來指定一個四邊形比較快。在一些更復雜的情形下,這是一種真正很有用的方法通過指定很多三角形來模擬複雜的表面。
一旦這個做了,我們就已經完成了drawScene函數。
        }
如果學到了這裏,確切地說,你可以準備開始實驗了。複製代碼到本地文件上,可以從GitHub得到或直接從現場演示網頁得到;如果你從演示網頁得到代碼,你需要index.html和glMatrix-0.9.4.min.js。確定在本地上能夠運行,然後嘗試改變上面的頂點座標;特別地,目前的場景是非常的平;嘗試將四邊形所有頂點的Z值改爲2或者-3,由於四邊形向前或向後移動,能夠看到它變大或變小。或者嘗試改變其中的一個或兩個,觀察一下它在透視中變扭曲。是不是快瘋了啊,不要介意我。我在這等着。
……
好的,既然你回來了,讓我們看看使得代碼運行的支持函數吧。正如我以前說的,如果你樂意忽視細節僅僅複製黏貼頁面中initBuffers函數以上的支持函數,你也許能夠不理它並且能製作出很有趣的WebGL頁面(儘管是白色和黑色——設置顏色在下一課).但是其中的詳細內容沒有難以理解的,通過理解這些代碼是怎樣運行的,你很有可能在以後寫出更好的WebGL代碼。
繼續下去?謝謝。讓我們首先看看這個礙事的最麻煩的函數吧。第一個是被webGLStart函數調用的initGL函數,它靠近頁面的頂部,這兒是代碼以供參考:
    var gl;
    function initGL(canvas) {
        try {
    gl = canvas.getContext("experimental-webgl");
    gl.viewportWidth = canvas.width;
    gl.viewportHeight = canvas.height;
        } catch(e) {
            }
    if (!gl) {
     alert("Could not initialise WebGL, sorry :-(");
        }
            }
這個非常簡單。你可能注意到了,前面談到的initBuffers函數和drawScene函數頻繁聯繫到一個叫“gl”的對象,很明顯這個對象聯繫到WebGL的“核心東西”。這個函數獲得了那些東西,被叫做WebGL文本的東西,通過請求文本中的canvas,然後使用一個標準的文本名,來獲得它。(正如你可能猜測的,在某種程度上這個文本名會從“experimental-webgl”改爲“webgl”;如果改了,我將會把這節教程更新並且在博客上說明相關信息)。一旦我們獲得了文本,我們再次使用JavaScript允許我們在任何一個對象上設置任何屬性的優點來存儲與畫布(canvas)相關的寬(width)和高(height);我們在drawScene函數開始的地方設置viewport和perspective中使用了這些屬性。完成上面的內容之後,我們的GL文本就設置好了。
調用initGL函數之後,webGLStart函數又調用initShaders函數。當然,這個函數是用來初始化渲染器。我們將會在後面返回來看它,因爲你需要先看看模型視圖矩陣,投影矩陣我在前面提到了。這裏是代碼:
    var mvMatrix = mat4.create();
     var pMatrix = mat4.create();
我們聲明一個mvMatrix變量來存儲模型視圖矩陣,聲明一個pMatrix變量存儲投影矩陣,然後設置它們爲空(都爲0)矩陣作爲開始。在這裏很值得更多地說明一下投影矩陣。你應該還記得,在drawScene函數的開始,我們將glMatrix函數mat4.perspective應用到這個變量來設置我們的perspective函數。這是因爲WebGL沒有內建perspective函數,就像WebGL不能直接支持模型視圖矩陣一樣。但是就像將物體的移動和旋轉封裝在模型視圖矩陣的處理方法一樣,使得遠處的物體看起來比近處的物體要適當小一點的處理方法是矩陣很擅長的功能。現在你會毫無疑問地推測,投影矩陣做這些事情。這個含有寬-高比和視野參數的mat4.perspective函數用我們想要的透視效果來填充矩陣。
好的,我們已經分析了所有代碼除了setMatrixUniforms函數和可怕的相關渲染器的代碼,我前面提到過setMatrixUniforms函數,它的作用是將模型視圖矩陣和投影矩陣從JavaScript移動到WebGL上。渲染器和這些函數是相互聯繫的,所以讓我們來看看相關背景知識吧。
現在來看看,什麼是渲染器呢?你可能會問。好的,在某種程度上在3D圖形歷史上渲染器跟它聽起來的意思一樣——一些能夠告訴系統在圖形繪製之前怎樣來着色或是渲染的代碼。然而,隨着時間的流逝,它們的範圍變大了,現在更好的定義它們爲:能夠繪製場景裏任何東西的代碼。這實際上是非常有用的,1、因爲它們能夠在顯卡上運行,所以它們運行起來確實快,2、它們能夠實現的變換是非常方便的甚至在這節課的簡單例子中。
我們在一節簡單的WebGL教程中介紹渲染器的原因(這在OpenGL中算是一個“中級”的教程了)是我們使用它來獲得WebGL系統,讓其在顯卡上運行,這樣我們場景的模型視圖矩陣和投影矩陣不需要在(相對)慢的JavaScript中移動每個頂點和每個向量。這是極其有用的,並且值得提供顯卡額外的開銷。
這兒是建立它們的代碼。你可能記得,webGLStart函數調用initShader函數,讓我們一行一行查看:
    var shaderProgram;
     function initShaders() {
         var fragmentShader = getShader(gl, "shader-fs");
         var vertexShader = getShader(gl, "shader-vs");
           shaderProgram = gl.createProgram();
              gl.attachShader(shaderProgram, vertexShader);
              gl.attachShader(shaderProgram, fragmentShader);
              gl.linkProgram(shaderProgram);
    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Could not initialise shaders");
         }
    gl.useProgram(shaderProgram);
正如你在上面看到的,它使用getShader函數得到兩個東西,一個“片段渲染器”和一個“頂點渲染器”,然後給它們倆附加上“program”。“program”是一些WebGL體系裏面的代碼;它能指定在顯卡上運行的東西。你可能會預料,我們可以將一些渲染器和“program”聯繫起來,在“program”裏能夠看到每個渲染器的片段代碼;確切地說,每一個“program”能夠儲存一個片段渲染器和一個頂點渲染器。我們來看看:
    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
一旦函數設置了“program”,附加上了渲染器,這個函數將傳遞一個參數給“attribute”,這個“attribute”被存儲在一個叫做“vertexPossitionAttribute”的“program”對象的新區域中。我們再一次利用了JavaScript可以給任何對象添加任何區域的特性;默認情況下“program”對象沒有vertexPositionAttribute區域,但是我們將這兩個值結合起來是很方便的,我們僅僅使得“attribute”變爲“program”的一個新的區域。
那麼vertexPositionAttribute是用來幹什麼的呢?你可能記得,我們在drawScene函數中使用了它;如果你返回去看設置存儲三邊形頂點位置的緩存的代碼,你能夠看到我們將緩存與這個屬性聯繫到了一起。你等會兒會明白這個意思;現在,你只需注意我們需要使用gl.enableVertexAttribArray來告訴WebGL我們想要提供數組數據給這個屬性。
    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
       }
initShaders函數最後要做的是從“program”中得到另外兩個數據——兩個一致變量。我們很快會碰到它;現在,你只需要注意,爲了方便,我們將像這樣的變量存儲在“program”對象中。
來看看getShader函數:
  function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
        return null;
    }
    var str = "";
    var k = shaderScript.firstChild;
    while (k) {
     if (k.nodeType == 3)
         str += k.textContent;
        k = k.nextSibling;
     }
     var shader;
     if (shaderScript.type == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
         return null;
    }
    gl.shaderSource(shader, str);
     gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
         alert(gl.getShaderInfoLog(shader));
         return null;
    }
        return shader;
     }
這是另一個比看起來更簡單的函數。我們所要做的是在HTML頁面中尋找一個具有和傳遞進來的參數相配的ID的元素,並提取它的內容,根據它的類型(在後面的教程會介紹它們之間的不同之處)創建一個片段渲染器或是頂點渲染器,然後將它們傳遞到WebGL中編譯爲顯卡能運行的數據。下面的代碼是處理錯誤的。當然,我們可以在JavaScript代碼中將渲染器定義爲一些字符串,這樣在HTML提取它們時不會看起來很亂——但是通過這種方法,我們使得渲染器很容易被瀏覽器讀取,因爲它們在頁面中被定義爲腳本,就好像它們是JavaScript腳本本身。
看過這些代碼之後,我們再來看看渲染器的代碼:
  <script id="shader-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision highp float;
     #endif
     void main(void) {
       gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
    </script>
    <script id="shader-vs" type="x-shader/x-vertex">
     attribute vec3 aVertexPosition;
     uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;
     void main(void) {
       gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    }
    </script>
第一個要注意的是這不是用JavaScript代碼寫的,儘管這個語言的最初版本和它非常相似。實際上,它們是用一門特殊的屬於C(當然,JavaScript也是)的渲染語言寫的。第一個渲染器,片段渲染器,其實沒有做什麼;它含有一些必要的模板代碼,告訴WebGL要多麼精確地使用浮點數,簡單地指定所有的東西將會被繪製成白色(怎樣添加顏色是第二課的內容)。第二個渲染器更有趣一點。它是頂點渲染器——你應該還記得,它是一些能夠隨意處理在顯卡上的頂點的代碼。它含有兩個一致變量uMVMatrix和uPMatrix。一致變量是非常有用的,因爲它們能夠進入到渲染器的外部——實際上,你可能記得當在initShader函數中抽取存儲單元的時候,一致變量可以從“program”裏面獲得數據,還可以從我們等下分析的代碼中獲得數據,(也許你知道是哪兒)我們將一致變量設置爲模型視圖矩陣和投影矩陣數據。你可能會把渲染器的“program”作爲一個對象(在面向對象程序設計裏),把一致變量作爲一個區域。
現在,渲染器被每個頂點調用,並且頂點數據作爲aVertexPosition傳入到渲染器中,多虧了在drawScene函數中使用了vertexPositionAttribute屬性,當我們將屬性和緩存聯繫在一起的時候。渲染器主程序中含有一點點代碼,用來將頂點和模型視圖矩陣、投影矩陣相乘,輸出最後頂點位置的結果。
綜上,webGLSart調用initShader、initShader函數加載頁面腳本中的片段渲染器和頂點渲染器,以至於相關代碼能夠被編譯,傳入到WebGL裏面,在渲染3D時被使用。
最後剩下的沒有解釋的代碼是setMatrixUniforms函數了,一旦你明白了所有上面的解釋,你就能很容易理解它了:
     function setMatrixUniforms() {
        gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
        gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
    }
獲得代表我們在initShaders函數中取回的模型視圖矩陣和投影矩陣的一致變量的參數,將這些數據以JavaScript形式的矩陣發送給WebGL。
哎呀!第一課的內容太多了,但是可喜的是你(還有我)已經明白了關於創建更有趣的東西的基礎工作,包括添加顏色、移動、一些3D WebGL模型。想要了解更多,請看第二課。

發佈了15 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章