iT邦幫忙

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

WebGL 與 Three.js 初探系列 第 6

[Day6] three.js 前置 webGL 修羅道(3) - 動畫與 varying

[Day6] webGL 修羅道(3) - Animation 與 varying

demo

昨天我們介紹完了如何傳遞資料給著色器,並且透過著色器畫了三個點,昨天有提到,我們可以透過 gl.drawArrays() 的方式告訴著色器頂點的連接方式。在進入今天的主題之前,先稍微說明一下 webGL 的繪圖方式。

  • gl.POINTS:僅繪製頂點
  • gl.LINES:從 buffer 中取兩點,並且繪製成線
  • gl.LINE_STRIP:每個頂點按照順序連起來
  • gl.LINE_LOOP:跟 LINE_STRIP 相同,但是最後一個頂點會連接回第一個頂點
  • gl.TRIANGLES:每次取三個點繪製三角形
  • gl.TRIAGLE_STRIP:從第三個頂點開始,每個頂點和前兩個連接
  • gl.TRIANGLE_FAN:從第三個頂點開始,每個頂點和第一個和上一個頂點連接

這張圖片可以很好的說明:

http://ithelp.ithome.com.tw/upload/images/20161220/20103565aMb8NujqbQ.jpg

從以上的說明可以發現,任何複雜的形狀在 webGL 裡頭都是由一連串的頂點座標,藉由三角形拼貼而成的。不過一般來說,建模的工作並不會直接用程式碼撰寫,而是由其他軟體輸出模型檔在傳給 webGL 繪圖。

Animation 簡介

demo2-布朗隨機運動

布朗隨機運動

布朗隨機運動 是一位羅伯特・布朗這位植物學家所發現的。當他在觀察水中的花粉時發現他們正在隨機運動,他發現就算水沒有在流動,這些花粉一樣不會停止運動。詳細的解釋過程可以直接到維基百科,我們這邊會利用這種隨成運動的方式來實現我們的動畫。

我們可以很容易在動畫中模擬這種效果,只要幫每個點座標加上一些隨機的偏移量就可以了。

function draw(gl, vertices) {
  
  // 布朗隨機運動
  for(var i = 0; i < POINTS_COUNT * 2; i += 2) {
    vertices[i] += Math.random() * 0.01 - 0.005; 
    vertices[i + 1] += Math.random() * 0.01 - 0.005;
  }
  
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.POINTS, 0, POINTS_COUNT);
  
  const bindDraw = (gl, vertices) => () => draw(gl, vertices);
  requestAnimationFrame(bindDraw(gl, vertices));
}

單純修改 vertices 陣列當中的值,並不會重新繪圖,我們必須重新傳入 buffer 新的頂點座標才行。不過有沒有看到這行程式碼呢?gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)

gl.STATIC_DRAW 代表我們常常會用到 buffer 的資料,但不會更動它的值。

顯然違反了我們的操作,所以我們必須修改為 gl.DYNAMIC_DRAW,告訴 webGL 裡頭的資料經常被修改。一旦設定之後,我們就不應該直接對 bufferData 做操作,而是使用 gl.bufferSubData 來傳入數據。

圓形分佈

我們當然也可以修改分布的方式。

var vertices = times(POINTS_COUNT * 2)
    .map((val, i) => Math.sin(Math.random()) *- 0.01);

實際動畫中的應用

在實務上的動畫中,這並不是一個常見的動畫調用方式,不過為了範例用途,就直接 show 給各位看了。一般表現動畫,我們通常會透過頂點與矩陣轉換的方式來操作,所以明天我們會進入 3D model 的應用。同時也會介紹 webGL 的一大利器,採樣器(sampler)來製作我們的紋理。

varying 應用

為了之後的 sampler 介紹,我們必須要介紹一下 GSGL 中的 varying。

之前各位所看到的 uniform 變數,我們將它套用到了 gl_FragColor 當中。所以我們的顏色都相同對吧!但實際的操作上,我可能會因為頂點的不同而採用不同的色彩。這時候 varying 就出現了。

varying 的作用是讓變數在 fragment shader 跟 vertex shader 中傳遞。這樣一來我們就可以依據頂點的位置加上不同的顏色。

來看一下我們的 fragment shader 跟 vertex shader:

<script id="shader-fs" type="x-shader/x-fragment">
    // shader program
    precision mediump float;
    varying lowp vec4 vColor;
    void main(void) {
      gl_FragColor = vColor;
    }
</script>
<script id="shader-vs" type="x-shader/x-vertex">
    attribute vec4 position;
    attribute float size;
    
    attribute vec4 aVertexColor;
    
    varying lowp vec4 vColor;
    
    void main(void) {
      gl_Position = position;
      vColor = aVertexColor;
    }
</script>

就可以利用 aVertexColor 傳遞值給 vColor 了。varying 變數會根據每個單獨的片段得到不同的顏色(進行差值)。注意到 varying 兩邊的變數名稱必須相同。

一樣,我們利用 gl.vertexAttribPointer 傳遞值給 buffer。結果像這樣:

http://ithelp.ithome.com.tw/upload/images/20161220/20103565ewnSWbQzLq.png
demo

那麼 webGL 的基本課程就到這邊結束了,明後兩天會做一些比較實際的應用,請大家敬請期待。

來不及說完的事

  • 紋理貼圖:我們前幾個範例當中,顏色幾乎都是基於頂點定義的。但更多時候,我們應該是將紋理放入我們的 3D 模型中。
  • 光源計算、隱影:在 GSGL 中可以做光源計算,不過礙於篇幅的關係,這邊並不細講,到 three.js 的時候,我們再來深入了解光源。
  • 深度測試與混合

不過接下來的兩天會用實際的應用來說明 webGL 的使用場景,希望能夠幫助各位對 webGL 有更深入的了解。

學習資源

webGL 幾乎可以算是全新的領域,對於前端來說可以擴展更多可能性。以下列出一些有用的教學資源,其實我們 webGL 要在一個禮拜內全部講完是不太可能的,不過這幾天的課程應該具備基本的 webGL 知識了。

  • webgl fundamental 教學很詳盡的網站,內容非常豐富,全部看完可以對 webGL 有更深入的了解
  • learning webgl 另外一個 webgl 的教學網站,可以學到蠻豐富的東西。
  • webgl 自學網 翻譯自 learning webgl 的英文網站。

上一篇
[Day5] WebGL 修羅道(2) - 資料傳遞
下一篇
[Day7] webGL修羅道(4) - 3D 與動畫
系列文
WebGL 與 Three.js 初探30

1 則留言

0
yiweitseng
iT邦新手 5 級 ‧ 2017-06-14 10:32:51

你好 目前看到Day 6 還是不了解 demo 中 gl.clear(gl.COLOR_BUFFER_BIT); 用法

/images/emoticon/emoticon02.gif

我要留言

立即登入留言