[WebGL] Lesson 3 - 繪製三角形Part2(Program & Buffer)

簡介

前一篇Part1我們已經介紹完如何建置Shader以及GLSL,緊接著我們要瞭解的是Program和Buffer的建置。


內文

所有GL繪製的程式碼都需要由Program來處理,而繪製的程式碼就是我們前一篇撰寫Shader的GLSL,這是很簡單的道理,若只有程式碼而沒有程序(Program)去驅動它的話,那也是無法繪製出任何東西(可以再回頭看看我繪製的流程圖)。Buffer在Lesson 1也介紹過了,它就是含有幾何圖形頂點位置以及顏色資料的暫存器,也就是說要繪製的圖形或物體的資料全都存在Buffer裡頭,而Program會從Buffer讀取資料後,再由Shader的GLSL來去處理這些資料,進而繪製出開發者想要的圖形或物體。


那麼,我們就先來看看如何建置Program吧!程式碼如下:

var program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('ERROR linking program!', gl.getProgramInfoLog(program));
        return;
    }

    gl.validateProgram(program);

if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) {
console.error('ERROR validating program!', gl.getProgramInfoLog(program));
        return;
}

首先.createProgram()這個函式就是用來建立程序(Program)並初始程序,非常淺顯易懂。而.attachShader()就如同上述所敘,要告知有哪些著色器(Shader)要交給程序來執行。至於.linkProgram()文獻上並沒有對此有太多的講解,可以視為硬體與程序之間的連結。接著後續就是查看程序連結狀況以及驗正程序是否有問題,這裡就不再多介紹了。

再來就是建立Buffer了,也就是我們要告知程序我們想繪製的圖形或物體了,而我們要繪製的圖形就是三角形,那麼我們就來在Buffer裡設置三角形的資訊吧!程式碼如下:


var triangleVertices = [
         0.0,  0.5,  0.0, //座標X,Y,Z
        -0.5, -0.5,  0.0,
         0.5, -0.5,  0.0
  ];

var triangleVertexBufferObject = gl.createBuffer();

gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexBufferObject);

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);

var positionAttribLocation = gl.getAttribLocation(program, 'aVertexPosition');

gl.vertexAttribPointer(
        positionAttribLocation, // Attribute location
        3, // Number of elements per attribute
        gl.FLOAT, // Type of elements
        gl.FALSE,
        3 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
        0 // Offset from the beginning of a single vertex to this attribute
);

gl.enableVertexAttribArray(positionAttribLocation);


首先我們來看triangleVertices這個陣列裡的元素,我們後續會告知程序(Program)是每三個元素一組來表示三維座標。而這裡的元素是以浮點數來表示座標,其座標系統如下圖所示:



.createBuffer()相信這個函式沒什麼好說明的,如同前述Program。.bindBuffer()主要是告知Buffer裡頭的資料是什麼類型,典型常見的就是GL_ARRAY_BUFFER或GL_ELEMENT_ARRAY_BUFFER,前者類型是用來表示頂點位置,後者類型是用來表點頂指數(之後會再說明)。.bufferData()第一個參數跟.bindBuffer()第一個參數是一樣的,有點重複的感覺,主要也是讓程序知道Buffer的資料屬性是為何,但是整體來看.bufferData()才是真正建立Buffer資料的函式,而第二個參數是告知資料型態,以float32Array資料型態存在Buffer中,意思就是陣列(Array)中每個元素都是32bits的浮點數型態,最後參數就是告知用途(Usage),gl.STATIC_DRAW意思就是資料只會被修改一次,但資料可以被多次繪製。

再來就是一個很重要的環節,Buffer的資料我們已經設置完畢,但是要怎麼讓程序(Program)知道該如何去應用Buffer裡的資料?讀到資料之後,又要怎麼去照GLSL程式碼來執行繪製?這個部分也不會太難,回去看看上頭的程式碼,var positionAttribLocation = gl.getAttribLocation(program, 'aVertexPosition');從函式名稱.getAttribLocation應該就可以了解,這個函式就是在讓程序知道頂點位置的資料要參照GLSL中宣告的哪一個變數,可以再回去看看我們寫的GLSL,點頂位置我們宣告的變數就是aVertexPosition。而.vertexAttribPointer每一個參數我已經有用註解的方式說明,這裡就不再多做介紹了。

終於,我們最後就是要來告知程序繪製的圖形,以及啟用程序,這個部分非常簡單,GL已經寫好這樣的函式,我們只需呼叫即可,程式碼如下:
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);

結果


參考

  1. Indigo Code 的Youtube教學影片
  2. Professional WebGL Programming: Developing 3D Graphics for the Web 原文電子書

留言