learing WebGL lesson 2 WebGL教程

歡迎來到我的第二課WebGL教程!這個時間我們來學習怎樣向場景裏面添加顏色。它是以Nehe的OpenGL教程第三課爲基礎的。
這兒是這節課的代碼在支持WebGL的瀏覽器運行看起來的效果:




一個小的忠告:這些教程所針對的人羣是具有一定的編程知識,但是沒有真正的3D圖形編程經驗;目的是使你入門並且知道這些代碼是怎樣運行的,以至於你能夠儘快開始製作自己的3D網頁。如果你還沒有學習第一課,在你學習這一課之前最好去學習一下第一課——這裏我將只解釋這節課與上節課不同的代碼。
依然,在這節教程中可能有bugs和錯誤的地方。如果你指出了一些錯誤,請在評論裏讓我知道,我將會盡快更正它。
有兩種方法你能夠得到這個示例的代碼:當你看現場演示的時候,查看源代碼或是如果你使用GitHub,你能夠複製它(還有以後的教程)從那個代碼庫。兩者選一,一旦你有了代碼,加載到你最喜歡的文本編輯器裏看一看。
大部分代碼和第一課是非常相似的。從頭至尾,我們:
    定義頂點渲染器和片段渲染器,使用含有"x-shader/x-vertex"和"x-shader/x-fragment"的<script>標籤;
    通過initGL函數初始化WebGL文本;
    使用getShader和initShaders函數將渲染器導入到WebGL program中;
    定義模型視圖矩陣mvMatrix函數,並通過實用函數調用loadIdentity、multMatrix、mvTranslate函數來操縱它;
    定義投影矩陣pMatrix函數,用一個透視實用函數perspective來操縱它;
    定義setMatrixUniforms函數來將模型視圖和投影矩陣推至JavaScript/WebGL設備上以至於渲染器能夠識別;
    使用initBuffers函數加載包含場景對象信息的緩衝;
    使用drawScene函數繪製場景;
    定義函數webGLStart設置好一切;
    最後,我們提供要求展示圖形的最少的HTML代碼。
與第一課代碼改變的地方是渲染器、initBuffers函數和drawScene函數。爲了解釋它們運行的改變,你需要知道一點關於WebGL的渲染管道。這兒是原理圖:



這個簡易圖顯示,在drawScene函數中傳入JavaScript函數的數據是怎樣轉變爲在屏幕中WebGL canvas裏顯示的像素的。它只是顯示了在這節課中需要解釋的步驟;我們將會在以後的教程中關注更加詳細的信息。
在最高的層次上,工作過程是這樣的:你每一次調用像drawArray這樣的函數,WebGL就會處理你之前以屬性的形式(像在第一課中設置的頂點緩存)和以一致變量的形式(我們用來組建投影和模型視圖矩陣)給它的數據,並且將這些數據傳到頂點渲染器。
在這個過程中,爲每一個頂點調用頂點渲染器,爲每一個頂點設置合適的屬性,一致變量也被傳入,但是正如它的名字所表示的,一致變量被調用的過程中它們沒有改變。頂點渲染器用這些數據來填充圖形——在第一課,頂點渲染器使用投影和模型視圖矩陣來使得這些頂點都能形成透視的效果,使得這些頂點根據我們的模型視圖狀態來移動——並且將這些數據轉化爲易變變量。頂點渲染器能夠輸出許多種類型的易變變量;有一種特殊的類型是必須輸出的,gl_positon,它包含頂點的座標當渲染器處理完後。
當頂點渲染器完成任務後,WebGL要求將易變變量所代表的3D圖像變爲2D圖像,然後WebGL爲圖像裏的每一個像素調用一次片段渲染器(在3D圖形系統裏,你也能聽到將片段渲染器稱爲像素渲染器,就是這個原因)。當然,這也意味着在圖像裏面不含有頂點的像素也會被片段渲染器處理。因此,WebGL在填充這樣的像素時採用了一種方法,叫做線性插值——對於組成三角形頂點的位置,這個過程是填充被頂點劃定的空間,這樣形成了一個可見的三角形。片段渲染器的目的是返回每一個插值點的顏色,並且是在叫gl_FragColor的易變變量中完成的。
一旦片段渲染器完成了這些,它的結果被WebGL處理了(我們將會在以後的課程中討論)並且它們被導入到幀緩存(frame buffer)裏面,幀緩存就是最終在屏幕上顯示的東西。
如果順利的話,到現在爲止這節課所教的怎樣讓頂點獲得顏色從JavaScript代碼一直到片段渲染器的重要技巧是非常清楚了,儘管現在還沒有分析具體的代碼。
我們使用的方法是利用這個事實:我們能夠在頂點渲染器外面傳入一些易變變量,不僅僅是頂點位置,然後再從片段渲染器中取回它們。因此,我們將顏色傳入頂點渲染器,然後直接轉化爲片段渲染器可拾取的易變變量。
很方便,我們可以直接得到顏色的線性變化。頂點渲染器計算頂點之間的片段時,所有的易變變量按線性插值的方法計算,不僅僅是位置。兩點之間的顏色線性插值是一個平滑的變化效果,就像你看到的上面那張圖片裏的三角形的效果。
讓我們來看看代碼;我們來看看與第一課不同的地方。首先,是頂點渲染器。它改變了很多,這兒是新代碼:
    attribute vec3 aVertexPosition;
    attribute vec4 aVertexColor;
    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;
    varying vec4 vColor;
  void main(void) {
      gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
      vColor = aVertexColor;
     }
這些代碼的意思是說我們有兩個屬性——不同頂點的輸入值——分別叫做aVertexPosition和aVertexColor,有兩個一致變量叫做uMVMatrix和uPMatrix,還有一個以易變變量的形式的輸出值叫做vColor。
在渲染器的主體部分,我們計算gl_Position(被定義爲每一個頂點渲染器的易變變量)實際上和第一課的方法相同,我們所做的是將顏色從輸入的屬性值轉化爲輸出的易變變量。
一旦每一個頂點都被這樣運行出來,插值用來運算之間的片段,這些都被傳入到片段渲染器:
    #ifdef GL_ES
    precision highp float;
     #endif
     varying vec4 vColor;
   void main(void) {
      gl_FragColor = vColor;
    }
這裏,在設置浮點精確度的模板代碼之後,我們獲取包含經過線性插值運算後的平滑混合顏色數據的易變變量vColor。
這是這節課與上節課在渲染器裏所有的不同。還有兩個其它的不同。第一個非常小;在initShaders函數中我們獲得兩個屬性的參數而不是一個;額外的一行代碼用紅色標記着:
  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);
       shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
       gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
       shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
       gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
       shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
       shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
     }
這個獲得屬性的代碼,在上一課中在某種程度上我已經解釋過,現在應該是十分清楚了:這是我們獲得想要傳入頂點渲染器爲每個頂點設置屬性的參數的辦法。在第一課,我們只是獲得頂點位置屬性。現在,足夠明顯,我們也得到了顏色屬性。
這一課剩下的改變是initBuffers函數,現在需要爲頂點位置和頂點顏色設置緩存了,還有drawScene函數,需要把它們兩個都傳入WebGL。
首先來看看initBuffers函數,我們定義新的全局變量來爲三角形和四邊形存儲顏色緩存:
    var triangleVertexPositionBuffer;
    var triangleVertexColorBuffer;
    var squareVertexPositionBuffer;
    var squareVertexColorBuffer;
然後,我們創建三角形頂點位置緩存之後,我們指明它的頂點顏色:
function initBuffers() {
    triangleVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    var vertices = [
         0.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);
    triangleVertexPositionBuffer.itemSize = 3;
    triangleVertexPositionBuffer.numItems = 3;
     triangleVertexColorBuffer = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexColorBuffer);
    var colors = [
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
     ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
    triangleVertexColorBuffer.itemSize = 4;
    triangleVertexColorBuffer.numItems = 3;
我們提供的顏色數據是一個數列,每一個值對應一個點,就像位置。然而,在這兩個數組緩存中有一個有趣的不同:一個頂點的位置可以被三個數指明,X、Y和Z座標,而每個頂點的顏色被四個元素所指明——紅、綠、藍和alpha值。Alpha,如果你對它不熟悉,是不透明度的一個度量值(0是透明的,1完全不透明),在後面的教程中是非常有用的。緩存中每項元素的改變導致需要改變相應的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;
    squareVertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexColorBuffer);
    colors = []
     for (var i=0; i < 4; i++) {
        colors = colors.concat([0.5, 0.5, 1.0, 1.0]);
     }
     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
    squareVertexColorBuffer.itemSize = 4;
    squareVertexColorBuffer.numItems = 4;
現在我們有了圖形的所有的數據在四個緩存中,下一個不同的地方是使得drawScene函數使用新的數據。新的代碼仍然用紅色標識,應該很容易理解:
   function drawScene() {
     gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
     perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
     loadIdentity();
    mvTranslate([-1.5, 0.0, -7.0])
     gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexColorBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, triangleVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
   setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);
    mvTranslate([3.0, 0.0, 0.0])
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexColorBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, squareVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
     setMatrixUniforms();
     gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
   }
下一個不同的地方……等一下,沒有下一個不同的地方!這就是在我們的WebGL場景裏添加顏色所必須有的,如果順利的話,你現在輕鬆的掌握了渲染器的基礎知識和它們之間數據的傳輸方法。
這就是這一課所有的內容——它比上一課要容易!如果你有任何的問題,評論,或者改正,請給我留言。
下一課,我們將在屏幕中添加點運動,旋轉三角形和四邊形。

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