昨天我們介紹完了如何傳遞資料給著色器,並且透過著色器畫了三個點,昨天有提到,我們可以透過 gl.drawArrays()
的方式告訴著色器頂點的連接方式。在進入今天的主題之前,先稍微說明一下 webGL 的繪圖方式。
這張圖片可以很好的說明:
從以上的說明可以發現,任何複雜的形狀在 webGL 裡頭都是由一連串的頂點座標,藉由三角形拼貼而成的。不過一般來說,建模的工作並不會直接用程式碼撰寫,而是由其他軟體輸出模型檔在傳給 webGL 繪圖。
布朗隨機運動 是一位羅伯特・布朗這位植物學家所發現的。當他在觀察水中的花粉時發現他們正在隨機運動,他發現就算水沒有在流動,這些花粉一樣不會停止運動。詳細的解釋過程可以直接到維基百科,我們這邊會利用這種隨成運動的方式來實現我們的動畫。
我們可以很容易在動畫中模擬這種效果,只要幫每個點座標加上一些隨機的偏移量就可以了。
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)來製作我們的紋理。
為了之後的 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。結果像這樣:
那麼 webGL 的基本課程就到這邊結束了,明後兩天會做一些比較實際的應用,請大家敬請期待。
three.js
的時候,我們再來深入了解光源。不過接下來的兩天會用實際的應用來說明 webGL 的使用場景,希望能夠幫助各位對 webGL 有更深入的了解。
webGL 幾乎可以算是全新的領域,對於前端來說可以擴展更多可能性。以下列出一些有用的教學資源,其實我們 webGL 要在一個禮拜內全部講完是不太可能的,不過這幾天的課程應該具備基本的 webGL 知識了。
你好 目前看到Day 6 還是不了解 demo 中 gl.clear(gl.COLOR_BUFFER_BIT); 用法