本節內容來自於小冊 WebGL 入門與實踐。javascript
上節帶領你們學習了基本三角形圖元的繪製過程,以及如何使用緩衝區
向着色器傳遞多個數據,但上節只演示了往着色器傳遞座標
這一種數據,本節經過繪製漸變三角形,講解一下如何經過緩衝區向着色器傳遞多種數據。html
本節經過一個鼠標每點擊三次便會繪製一個漸變三角形的示例,帶你們深刻理解緩衝區的用法,最終效果以下圖所示: 前端
經過本節學習,你將會掌握以下內容:java
buffer
中的排布方式。buffer
時,bindBuffer
的重要性。buffer
讀取多種頂點數據。buffer
讀取多種頂點數據。上節咱們實現的是單色三角形,經過在片元着色器中定義一個 uniform
變量,接收 JavaScript 傳遞過去的顏色值來實現。那漸變三角形的處理與單色三角形有何不一樣呢?git
漸變三角形顏色不單一,在頂點與頂點之間進行顏色的漸變過渡,這就要求咱們的頂點信息除了包含座標
,還要包含顏色
。這樣在頂點着色器以後,GPU 根據每一個頂點的顏色對頂點與頂點之間的顏色進行插值,自動填補頂點之間像素的顏色,因而造成了漸變三角形。github
那既然咱們須要爲每一個頂點傳遞座標信息和顏色信息,所以須要在頂點着色器中額外增長一個 attribute
變量a_Color
,用來接收頂點的顏色,同時還須要在頂點着色器和片元着色器中定義一個 varying 類型的變量v_Color
,用來傳遞頂點顏色信息。web
//設置浮點數精度爲中等精度。
precision mediump float;
//接收頂點座標 (x, y)
attribute vec2 a_Position;
//接收瀏覽器窗口尺寸(width, height)
attribute vec2 a_Screen_Size;
//接收 JavaScript 傳遞的頂點顏色
attribute vec4 a_Color;
//傳往片元着色器的顏色。
varying vec4 v_Color;
void main(){
vec2 position = (a_Position / a_Screen_Size) * 2.0 - 1.0;
position = position * vec2(1.0,-1.0);
gl_Position = vec4(position, 0, 1);
v_Color = a_Color;
}
複製代碼
片元着色器新增一個 varying 變量 v_Color
,用來接收插值後的顏色。編程
//設置浮點數精度爲中等。
precision mediump float;
//接收 JavaScript 傳過來的顏色值(rgba)。
varying vec4 v_Color;
void main(){
vec4 color = v_Color / vec4(255, 255, 255, 1);
gl_FragColor = color;
}
複製代碼
咱們的着色器部分仍是和以前同樣簡單,只是在頂點着色器中增長了頂點顏色這一變量。canvas
接下來咱們用 JavaScript 向着色器傳遞數據。數組
用緩衝區向着色器傳遞數據有兩種方式:
上節繪製三角形的時候咱們給頂點着色器傳遞的只是座標信息,而且只用了一個 buffer
,本節示例,咱們除了傳遞頂點的座標數據,還要傳遞頂點顏色。 按照正常思路,咱們能夠建立兩個 buffer
,其中一個 buffer
傳遞座標,另一個 buffer
傳遞顏色。
建立兩個 buffer
,將 a_Position
和 positionBuffer
綁定,a_Color
和 colorBuffer
綁定,而後設置各自讀取 buffer
的方式。
請謹記:程序中若是有多個
buffer
的時候,在切換buffer
進行操做時,必定要經過調用gl.bindBuffer
將要操做的buffer
綁定到gl.ARRAY_BUFFER
上,這樣才能正確地操做buffer
。您能夠將bindBuffer
理解爲一個狀態機,bindBuffer
以後的對buffer
的一些操做,都是基於最近一次綁定的buffer
來進行的。
如下 buffer
的操做須要在綁定 buffer
以後進行:
- gl.bufferData:傳遞數據。
- gl.vertexAttribPointer:設置屬性讀取 buffer 的方式。
咱們使用一個 buffer 傳遞座標信息,另外一個 buffer 傳遞顏色信息。
// 建立 座標信息 buffer
var positionBuffer = gl.createBuffer();
// 將當前 buffer 設置爲 postionBuffer,接下來對 buffer 的操做都是針對 positionBuffer 了。
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 設置 a_Position 變量讀取 positionBuffer 緩衝區的方式。
var size = 2;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(
a_Position, size, type, normalize, stride, offset);
// 建立 顏色信息 buffer
var colorBuffer = gl.createBuffer();
// 將當前 buffer 設置爲 postionBuffer,接下來對 buffer 的操做都是針對 positionBuffer 了。
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
// 設置 a_Position 變量讀取 positionBuffer 緩衝區的方式。
var size = 4;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(
a_Color, size, type, normalize, stride, offset);
複製代碼
gl.vertexAttribPointer( a_Color, size, type, normalize, stride, offset)。這個方法比較重要,上節已經向你們詳細介紹了,若是還不太明白的,能夠再次回顧下上節內容。
咱們發現,上面代碼對 buffer
的操做有些冗餘,咱們仍是提取出一個方法 createBuffer
放到 webgl-helper.js
,減小重複編碼,以後咱們對 buffer
的一系列調用只須要以下兩句就能夠了:
var positionBuffer = createBuffer(gl, a_Position, { size: 2});
var colorBuffer = createBuffer(gl, a_Color, { size: 4});
複製代碼
假如咱們頂點座標數組中有四個頂點 8 個元素【30, 30, 30, 40, 40, 30, 20, 0】,頂點着色器中的 a_Position
屬性在讀取頂點座標信息時,以 2 個元素爲一組進行讀取:
又假如咱們頂點顏色數組中有兩個頂點 8 個元素 【244, 230, 100, 1, 125, 30, 206, 1】,那麼頂點着色器中的 a_Color
屬性在讀取頂點顏色信息時,以 4 個元素(r, g, b, a)爲一組進行讀取,以下圖所示。
以多少元素做爲一個頂點信息進行讀取的設置,是在調用
gl.vertexAttribPointer
時設置的size
參數值。
言歸正傳,接下來咱們爲 canvas
添加點擊事件:
canvas.addEventListener('click', e => {
var x = e.pageX;
var y = e.pageY;
positions.push(x, y);
//隨機一種顏色
var color = randomColor();
//將隨機顏色的 rgba 值添加到頂點的顏色數組中。
colors.push(color.r, color.g, color.b, color.a);
//頂點的數量是 3 的整數倍時,執行繪製操做。
if (positions.length % 6 == 0) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.DYNAMIC_DRAW);
render(gl);
}
})
複製代碼
萬事俱備,只欠繪製:
function render(gl) {
//用設置的清空畫布顏色清空畫布。
gl.clear(gl.COLOR_BUFFER_BIT);
if (positions.length <= 0) {
return;
}
//繪製圖元設置爲三角形。
var primitiveType = gl.TRIANGLES;
//由於咱們要繪製三個點,因此執行三次頂點繪製操做。
gl.drawArrays(primitiveType, 0, positions.length / 2);
}
複製代碼
至此,三角形的漸變效果就實現啦。
常規思路使用多個 buffer
傳遞多種數據(座標和顏色),咱們再演示另一種思路:使用 1 個 buffer
同時傳遞多種數據。
着色器部分的代碼和上面的同樣,無需改動,改動的主要部分是 JavaScript 程序。
首先,咱們依然是建立 buffer
,只不過此次是建立一個 buffer
。
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
複製代碼
建立完 buffer
,接下來設置讀取 buffer
的方式,咱們有兩個屬性 a_Position
、a_Color
,因爲咱們只有一個 buffer
,該 buffer
中既存儲座標信息,又存儲顏色信息,因此兩個屬性須要讀取同一個 buffer
:
咱們能夠看到,一個頂點信息佔用 6 個元素,前兩個元素表明座標信息,後四個元素表明顏色信息,因此在下面設置屬性讀取 buffer
方式時,a_Color
和 a_Position
的設置會有不一樣:
a_Position:座標信息佔用 2 個元素,故 size 設置爲 2。 座標信息是從第一個元素開始讀取,偏移值爲 0 ,因此 offset 設置爲 0.
a_Color:因爲 color 信息佔用 4 個元素,因此 size 設置爲 4 。 color 信息是在座標信息以後,偏移兩個元素所佔的字節(2 * 4 = 8)。因此,offset 設置爲 8。
stride:表明一個頂點信息所佔用的字節數,咱們的示例,一個頂點佔用 6 個元素,每一個元素佔用 4 字節,因此,stride = 4 * 6 = 24 個字節。
gl.vertexAttribPointer(
a_Color, 4, gl.FLOAT, false, 24, 8);
gl.vertexAttribPointer(
a_Position, 2, gl.FLOAT, false, 24, 0);
複製代碼
canvas
的點擊事件也有所不一樣,一個頂點佔用 6 個元素,三個頂點組成一個三角形,因此咱們的 positions
的元素數量必須是 18 的整數倍,才能組成一個三角形:
canvas.addEventListener('click', e => {
var x = e.pageX;
var y = e.pageY;
positions.push(x);
positions.push(y);
//隨機出一種顏色
var color = randomColor();
//將隨機顏色的 rgba 值添加到頂點的顏色數組中。
positions.push(color.r, color.g, color.b, color.a);
//頂點的數量是 18 的整數倍時,執行繪製操做。
if (positions.length % 18 == 0) {
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
render(gl);
}
})
複製代碼
實現效果和上面操做多緩衝區的方式同樣,可是單緩衝區不只減小了緩衝區的數量,並且減小了傳遞數據的次數以及複雜度。
至此,咱們對緩衝區的講解就結束了,本節所講知識點和上節基本相似,不一樣點在於用單個緩衝區傳遞多類數據時,gl.vertexAttribPointer
各個參數如何設置,理解這點對咱們之後編程十分有用,但願你們課下多多練習,深入理解它的用法。
到目前爲止,咱們掌握了三角形的繪製方法,接下來學習怎樣用三角形構建其餘圖形。
下一節咱們將從簡單平面開始:先用三角形構建一個矩形。
這一系列的內容來自於小冊 WebGL 3D 入門與實踐,若是你們對進階知識感興趣,能夠到小冊中去學習:小冊:WebGL 3D 入門與實踐。
小冊內容除了包含 WebGL 相關的基礎練習,還包括 3D 圖形概念與相關數學的原理與推導,旨在幫助你們創建圖形學的技術輪廓。這部分圖形學知識獨立於 WebGL,除了能夠適用於 WebGL,還適用於 OpenGL 等。
固然,本小冊主要目的仍是幫助 Web 前端同窗學習 3D 技術,除了介紹適用 WebGL 實現 3D 效果之外,還對 CSS3 中的 3D 技術相關屬性進行了深刻剖析,並演示了與數學庫的結合使用,但願可以讓前端同窗不只僅侷限於通常的二維平面開發,也可以在 3D 開發上更近一步~