iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 5
0
Modern Web

WebGL 與 Three.js 初探系列 第 5

[Day5] WebGL 修羅道(2) - 資料傳遞

上一篇文章中,我們將 gl_Positiongl_PointSize 等變數寫死在 shader 裡頭,這顯然不是個好方法,光是看到 C/C++ like 的程式碼就覺得頭大了,竟然還要我每天修改這些值。如果可以用傳參數的方式來做呢?

這時候就需要用到 shader 中一個資料型別 attribute,如果還記得上一篇文章中我們講到,shader 有四種接收資料的方式,

buffers 跟 attributes: 我們可以將資料放進去 buffer 當中,當 GPU 需要使用時就從 buffer 取用,通常會存放像是頂點位置、紋理、顏色等等的資料;attribute 則代表我們如何從緩衝區中獲得資料

範例程式碼在這:demo

修改 shader

<script id="shader-fs" type="x-shader/x-fragment">
    // shader program
    precision mediump float;
    uniform vec4 color;
    void main(void) {
      gl_FragColor = color;
    }
  </script>
  
  <script id="shader-vs" type="x-shader/x-vertex">
    // 我們在這裡撰寫 GSGL
    // vec4 代表一個向量,接收四個參數(x,y,z,w)
    // 0,0,0 在 3d 座標中代表中心
    
    attribute vec4 position;
    attribute float size;
    
    void main(void) {
      gl_Position = position;
      gl_PointSize = size;
    }
  </script>

我們修改 shader 中的程式碼,並且將原本的常數值修改為變數,這樣一來我們就可以在 js 當中傳值了。

我們新增了一個 function 叫作 createPoints,呼叫 gl.getAttribLocation 方法,可以拿到 shader 中的變數。

而傳入值的話,則是使用 gl.vertexAttrib3f ,要注意 attribute 這個型別只能夠用在 vertex shader 中!其實在使用 attribute,是先取用 buffer 中的資料在傳給 attribute,我們待會會講到。剛剛的範例當中,我們只是簡單傳入了一個頂點(0,0,0)給 GPU,但實際的場景中。

再來說說 uniform,uniform 一旦給予值之後就不會再變化,對我們來說,我們目前的顏色在每個點上都是相同的,所以使用 uniform。

function createPoints(gl, program) {
  var points = gl.getAttribLocation(program, "position");
  var size = gl.getAttribLocation(program, "size");
  
  gl.vertexAttrib3f(points, 0, 0, 0);
  gl.vertexAttrib1f(size, 100.0);
  
  var color = gl.getUniformLocation(program, "color");
  gl.uniform4f(color, 0,1,0,1);
}

Buffer

前面我們傳入了一個點給 GPU,並且將他畫出來,這顯得有點無聊。我們不是要畫一些複雜一點的東西嗎?沒錯,所以現在要介紹 buffer。

Buffer 代表代表緩衝區,能夠將一連串的資料傳入 GPU,繪圖時如果有需要時可以隨時取用。

所以我們修改 createPoints 如下:

function createPoints(gl, program) {
  var verteices = [
    0.9, -0.9,0.0,
    -0.9, 0.9, 0.0,
    0.0,0.9,0.0
  ];
  
  // 我們現在有了三個點座標,現在將它放入 buffer 當中
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verteices), gl.STATIC_DRAW);
  
  var points = gl.getAttribLocation(program, "position");
  var size = gl.getAttribLocation(program, "size");
  
  // gl.vertexAttrib3f(points, 0, 0, 0);
  // 使用 gl.vertexAttribPointer 來指向指標
  // 我們需要告訴它,每個頂點將有三個值 X,Y,Z ,它們是浮點數。
  // 後面三個參數分別為 normalized, type stride
  // 之後會再討論,暫時先不理他
  gl.vertexAttribPointer(points, 3, gl.FLOAT, false, 0 , 0);
  gl.enableVertexAttribArray(points);
  
  gl.vertexAttrib1f(size, 100.0);
  
  var color = gl.getUniformLocation(program, "color");
  gl.uniform4f(color, 0,1,0,1);
}

  • gl.bindBuffer(target, buffer) 它將查看什麼緩衝區綁定到該數組緩衝區目標,並使用它。
  • gl.bufferData(target, data, usage) 將數據放入緩衝區。gl.ARRAY_BUFFER 告訴 WebGL 用於此數據的目標緩衝區。gl.STATIC_DRAW 參數告訴 WebGL 我們打算如何使用數據。這個選項說,我們會經常使用這些值,但我們不會改變它們。

現在我們有一個完整的緩衝區充滿了數據,現在要傳遞的不是一個值,而是一個指向該緩衝區的指針。所以利用了 gl.vertexAttribPointer,還要記得啟動 gl.enableVertexAttribArray

現在來看看我們的 draw function。原本的 count 是 0,現在我們將它改為 3,我們能夠畫出三個點來了!這三個點的座標是從 buffer 當中依照順序拿取,可以修改看看 count 的值或是在 vertices 當中加入更多的點座標看看效果為何。

function draw(gl) {
  gl.clear(gl.COLOR_BUFFER_BIT);
                          //  start, count
  gl.drawArrays(gl.POINTS, 0, 3);
}

drawArray 的第一個參數決定 webGL 如何畫畫,可以參考webGL Constant 看看有什麼更多的資訊吧!

結論

目前為止,我們已經了解了 webGL 的基本 API 了。讓我們複習一下:

  • 在 createShaders 當中,我們使用 gl.createShader 的方式建立 shader,並且使用 gl.shaderSourcegl.compileShade 將我們撰寫的 GSGL 程式碼連結至 webGL 當中。
  • initShaders 當中,我們使用 gl.createProgram 以及 gl.attachProgram 將 fragment shader 以及 vertex shader 傳入。最後再用 gl.linkProgramgl.useProgram 完成整個 program 的編譯。
  • createPoints 當中,我們用到了 getUniformLocation getAttribLocation 讓 webGL 跟 GSGL 的變數做連結。並且使用了 gl.vertexAttrib1f gl.uniform4f 的方式來傳值進去。
  • webGL 接收資料的方式有幾種:attribute buffer uniform varying 除了 varying 之外,我們已經簡介過其他資料型別的使用了。

再來我們更深入的看看到底能透過這些 API 做什麼應用吧!


上一篇
[Day4] WebGL 修羅道(1) - 用 100行程式碼畫...一個點
下一篇
[Day6] three.js 前置 webGL 修羅道(3) - 動畫與 varying
系列文
WebGL 與 Three.js 初探30

尚未有邦友留言

立即登入留言