優雅的學習webgl(2)—webgl中的着色器和緩衝區


    在以前一章中咱們瞭解了webgl中的一些常識,也嘗試繪製了第一個webgl程序,接下來這一張會介紹一些webgl中的基本概念,包含頂點着色器、片元着色器、緩衝區git

  • 什麼是頂點着色器和片元着色器
  • 緩衝區

這個系列的源碼地址爲:源碼的地址爲: https://github.com/forthealll...github

1、什麼是頂點着色器和片元着色器

    咱們在第一篇文章中初略的介紹了着色器,在這裏咱們在詳細講講什麼是着色器。web

1.頂點着色器

    頂點着色器保存了所想繪製的圖形的頂點的信息,包括了頂點的座標,以及頂點的大小。咱們結合GLSL語言,來詳細的講講頂點着色器。數組

attribute vec4 a_position;
 
void main() {
   gl_Position = a_position;
   gl_PointSize = 10.0;
}

    頂點着色器基本上具備兩個系統的變量gl_Position和gl_PointSize,分別表示頂點的位置和頂點的大小。而且圖形中的每個頂點都會調用一次頂點着色器。頂點着色器可使用attribute來修飾的全局變量。瀏覽器

上述例子中的:緩存

attribute vec4 a_position;

    a_position變量就是一個attribute修飾符修飾的全局變量,其變量的值能夠在其餘地方被賦值。咱們能夠在咱們的js代碼中:性能

var positionLoc = gl.getAttribLocation(someShaderProgram, "a_position");

    經過gl實例來獲取某個程序對象上的attribute修飾符修飾的變量a_position的地址,也能夠給這個a_position變量賦新的值。webgl

gl.vertexAttribPointer(positionLoc,newValue)

這裏的全局變量聽起來很拗口,其實全局的意思就是:spa

不只僅只能在着色器中被使用,能夠在着色器外被捕獲或者誰用的變量,在GLSE語言中都是全局變量。插件

在頂點着色器中使用的所有變量除了用attribute修飾外,還能夠經過uniform來修飾。好比咱們須要在移動某個圖形(給某個圖形的每一個頂點一個偏移)

//頂點着色器
attribute vec4 a_position;
uniform vec4 u_offset;
 
void main() {
   gl_Position = a_position + u_offset;
}
//頂點着色器之外的邏輯代碼
var offsetLoc = gl.getUniformLocation(someProgram, "u_offset");
gl.uniform4fv(offsetLoc, [1, 0, 0, 0]);  // 向右偏移一半屏幕寬度

    咱們經過uniform這個全局變量,給頂點着色器賦值了一個偏移量。

在頂點着色器中attribute修飾的變量和uniform修飾的變量均可以做爲全局變量在着色器外被取值和賦值。那麼二者以前有什麼區別?

attribute修飾的全局變量能夠讀取緩衝區中的值

至於什麼是緩衝區,咱們下節中會具體降到,簡單來講緩衝區的存在使得能夠在一次繪製中繪製出圖形全部的頂點,而不須要手動的逐頂點繪製.

2.片元着色器

    在前面一章中咱們講到片元着色器決定了圖形的顏色,那麼片元着色器是如何動做的呢。簡單來講:

從頂點着色器中瞭如何裝配圖形,當頂點着色器畫出了圖形的輪廓後,片元着色器將圖形分解成一個個小的片斷,而且肯定每個片斷的顏色。最後渲染出結果。

    其實從頂點着色器到片元着色器,而後從片元着色器到渲染到瀏覽器的過程比較複雜,具體能夠分爲以下幾步:

  • 初始化頂點着色器
  • 根據頂點着色器中的頂點值,開始圖形裝配
  • 圖形裝配完成後,開始光柵化
  • 光柵化後每個片元均可以用內置變量gl.FragCoord來表示
  • 從片元(Frag)相對應的gl.FragCoord中讀取信息並對每一個片元生成獨立的顏色信息gl.FragColor,並保存到顏色緩衝區
  • 當全部的片元都有有顏色信息後,將顏色緩衝區的圖形渲染到瀏覽器中

    用文字表述可能不夠直觀,咱們來用圖形表示:

Lark20191206-202215

    咱們以渲染一個正方形爲例,在頂點着色器中咱們只給出了4個頂點的座標,光柵化後生成了9個小片元,光柵化後的小片元進入片元着色器中被處理,最後渲染到瀏覽器中。

    咱們在上一節中發如今片元着色器中咱們僅僅傳入了4個頂點的顏色,可是經過片元着色器,最後渲染出來的圖形是一個彩色的正方形。

                Lark20191206-203133

    咱們從頂點着色器中能夠傳遞4個頂點的顏色信息給片元着色器,片元着色器接受這4個頂點的顏色信息,經過內插能夠拿到每一個片元的信息,最後渲染到瀏覽器中。

    咱們來了解一下內插的過程,首先咱們能夠經過varying修飾的變量將值從頂點着色器傳遞到片元着色器。

//頂點着色器

attribute vec4 a_color
varying vec4 v_color
void main() {
   v_color = a_color
}

//片元着色器

varying vec4 v_color
void main(){
   gl.FragColor = v_color
}

    咱們經過聲明兩個同名的用varying修飾的變量v_color,就能夠實現將值從頂點着色器傳遞到片元着色器。

    爲何咱們須要將值從頂點傳遞到片元着色器呢?

由於片元着色器的顏色信息等可能與頂點的座標有關,此外頂點着色器是惟一能夠從緩存中讀取值的地方.

咱們接着來看插值的過程示意圖:

Lark20191209-172415

內插發生在光柵化的時候,能夠根據頂點的顏色值,內插獲得全部片元的顏色值。簡單距離好比兩個頂點的顏色分別是(0,0,0)和(0,0,1),兩個頂點間有10個片元,那麼內插獲得的幾個片元的顏色分別爲(0,0,0.1),(0,0,0.2)...(0,0,0.9)

    總之:經過頂點着色器和片元着色器咱們就能繪出各類圖案,頂點着色器和片元着色器是webgl繪圖的基礎.

2、緩衝區

    緩衝區的做用很簡單,就是緩衝圖形的信息,在一次繪製中將圖形繪製出來。咱們接下來一步步來看緩衝區,咱們前面畫了一個點:

const vsSource = `
    attribute vec4 a_Position;
    void main() {
      gl_Position = a_Position;
      gl_PointSize = 10.0;
    }
  `;

  // Fragment shader program

  const fsSource = `
    void main() {
      gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
  `;
  const shaderProgram = initShaderProgram(gl, vsSource, fsSource)
  gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
  gl.clearDepth(1.0);                 // Clear everything
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  gl.useProgram(shaderProgram);
  //開始繪製
  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,0.0,0.0);
  gl.drawArrays(gl.POINTS,0,1);

上述代碼的運行結果爲:

Lark20191209-175444

若是咱們要繪製3個點,那麼須要修改上述的代碼:

//開始繪製
  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,0.0,0.0);
  gl.drawArrays(gl.POINTS,0,1);

  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,0.1,0.0);
  gl.drawArrays(gl.POINTS,0,1);

  a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  gl.vertexAttrib3f(a_Position,0.0,-0.1,0.0);
  gl.drawArrays(gl.POINTS,0,1);

咱們須要連續繪製3次才能出現3個點,繪製的結果以下圖所示:

Lark20191209-182151

    對於複雜的圖形,咱們不可能一個點一個點的去繪製,若是咱們須要一次性的繪製多個點,那麼就須要使用緩衝區,咱們能夠讀取緩衝區內的頂點信息,而後一次性的繪製出來。

    緩衝區是webgl系統中的一塊存儲區,能夠在緩衝區對象保存繪製圖形所須要的頂點信息。

使用緩衝區須要一下的五個步驟:

  1. 建立緩衝區(gl.createBuffer())
  2. 綁定緩衝區(gl.bindBuffer())
  3. 將數據寫入緩衝區對象(gl.bufferData())
  4. 將緩衝區對象分配給一個attribute變量(gl.vertexAttribPointer())
  5. 開啓attribute變量(gl.enableVertexAttribArray())

    遵循上述的5個步驟,咱們來使用緩衝區一次性繪製三個點,修改上面的代碼:

const shaderProgram = initShaderProgram(gl, vsSource, fsSource)
  gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
  gl.clearDepth(1.0);                 // Clear everything
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  gl.useProgram(shaderProgram);
  //開始繪製
  let vertices = new Float32Array([0.0,0.0,0.0,0.1,0.0,-0.1])  ---(1)
  //建立緩衝區
  let vertexBuffer = gl.createBuffer()                 ----(2)
  //將緩衝區對象綁定到目標
  gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer)          ----(3)
  //向緩衝區對象寫入數據
  gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW)      ---(4)
  let a_Position = gl.getAttribLocation(shaderProgram,'a_Position')
  //將緩衝區對象分配給a_Position
  gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0)     ---(5)
  //開啓attribute變
  gl.enableVertexAttribArray(a_Position);
  gl.drawArrays(gl.POINTS,0,3)

上述的代碼一樣的是繪製3個點,可是是在一次繪製中就完成了。具體的代碼能夠查看:https://github.com/forthealll...

來看幾個須要注意的地方,首先在(1)中的Float32Array是一個類型化數組,類型化數組是JavaScript操做二進制數據的一個接口,並不須要引入其餘任何插件。
webgl中須要瀏覽器和顯卡進行通訊,爲了知足JavaScript與顯卡之間大量的、實時的數據交換,它們之間的數據通訊必須是二進制的,而不能是傳統的文本格式。像C語言那樣,直接操做字節,而後將4個字節的32位整數,以二進制形式原封不動地送入顯卡,腳本的性能就會大幅提高。

let vertices = new Float32Array([0.0,0.0,0.0,0.1,0.0,-0.1])

此外就是將緩衝區對象分配給a_Position的(5)方法gl.vertexAttribPointer,該方法的接受6個參數,具體參數的意思這裏不會去講,值得注意的是這個方法將緩衝區的值傳遞給了頂點着色器中的attribute變量,並聲明瞭如何在緩衝區中依次如何取值的方法。

gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0)

    這裏這6個參數的意思是,緩衝區的attribute變量的引用a_Position,每次從緩衝區取2個點,緩衝區值的類型是FLOAT類型,false表示不會歸一化,0表示從緩衝區數組的第0數組開始取,最後一個0表示間隔爲0.

    上述就是緩衝區的作用,上述的緩衝區只保存了頂點信息,實際上緩衝區能夠保存頂點的座標信息,顏色信息,光照信息,矩陣變換信息等等,同時緩衝區也能夠有多個,頂點着色器可使用attribute變量來讀取緩衝區的值,同時片元着色器能夠經過varying變量來接受從頂點着色器傳遞過來的值,從而在依次繪製中就能夠獲得完整的結果。

    最後總結一下緩衝區和頂點着色器、片元着色器之間的通訊:

Lark20191209-203239

相關文章
相關標籤/搜索