WebGL學習系列 使用緩衝區物件畫多個點

2021-09-19 12:36:07 字數 3665 閱讀 3996

一般而言,我們需要繪製的點的數量非常的多,所以不可能像第乙個程式一樣乙個點乙個點繪製,webgl提供了緩衝區物件,用於處理繪製多個點的資料問題。

座標系為了更好的理解點的位置,我們需要知道canvas以及webgl的座標系。

canvas座標系如下所示: 

webgl座標系如下所示: 

可以看到,webgl的座標系跟canvas座標系是不一樣的,原點位置不一樣,定義也不太一樣,canvas是以畫素值為座標值的,在webgl當中,不管依託的canvas寬高大小,它四個頂點的座標值都是不變的,而且取中心點為原點。

向量上一小節,我們使用 vec4(0.0, 0.0, 0.0, 1.0); 來表示點的位置,vec4表示有4個數值的向量,當把vec4賦值給頂點位置的時候,前三個引數分別表示xyz座標,那為何還有第四個引數呢?其實,在圖形學中,通常使用稱為齊次座標的形式來表示座標,如(x,y,z,w),它相當於 (x/w,y/w,z/w),主要是為了便於計算(後續章節將會逐步體會到)。所以現在你知道為何我們定義第4個引數為1.0了吧。

此外,我們使用 vec4(1.0, 0.0, 0.0, 1.0) 來表示紅色,四個引數對應rgba四個分量值,只不過以往我們使用0~255來表示rgb三個分量,在webgl我們把它歸一化了,其範圍值為0.0~1.0。

著色器變數和緩衝區物件

還記得咱們畫乙個點的時候 ,在頂點著色器**中,固化了頂點的值,現在,我們想要畫多個點,難道要建立好多個program和好多個頂點著色器麼?顯然不可能這麼麻煩。為了解決這個問題,webgl提供了緩衝區物件,使得我們可以使用乙個陣列來連續存放多個頂點的資料,然後使用drawarrays一次性畫出來,這時候,我們要引入著色器變數才行。

先來看一下本次使用的頂點著色器**:

// 頂點著色器**(決定頂點在**,大小)

var vshader_source = 

'attribute vec4 a_position;\n' +

'void main() \n';

可以看到 attribute vec4 a_position; 這一行**,它表示定義乙個著色器變數,型別為vec4,變數名字為a_position,然後在main方法中直接把 a_position 賦值給gl_position。

讓我們再強調一次,每執行一次頂點著色器**,只可以獲取乙個頂點的資訊,為了獲取多個不同的頂點資訊,每呼叫一次頂點著色器**的時候,a_position必須是要不一樣的,也就是動態值,結合緩衝區技術我們便可以實現 。

我相信,你依然會有疑惑這一切是怎麼做到的,先來看看緩衝區使用示意圖:

不要被嚇到了,我們一步步來看,首先記得我們的目標是要利用同乙個頂點著色器來繪製多個頂點,本例我們繪製三個頂點。

1、準備頂點資料,我們要繪製三個頂點,所以需要3個頂點的資料,如下:

// float32array 是用於存放 float 型別的陣列,每乙個float資料占用4個位元組

var vertices = new float32array([

0.0, 0.5,   -0.5, -0.5,   0.5, -0.5

]);注意,由於目前我們使用的座標z值為1,w=1,所以只定義x和y座標就可以了,以上就是三個頂點位置的定義。

2、建立乙個緩衝區物件

// 建立緩衝區物件非常的簡單

var vertexbuffer = context.createbuffer();

3、把緩衝區物件繫結到 array_buffer 目標上 

我想看到這裡很多人會有疑問,啥是目標物件呀?回頭看看第1和第2這兩個步驟,我們準備好了頂點資料以及 乙個空的緩衝區物件,那麼怎麼把頂點資料放入到緩衝區物件呢?webgl沒有直接的方法可以做到,只能夠借助乙個中間**,這就是繫結的由來。我們暫時把空的緩衝區物件繫結到 array_buffer 這個目標上(目標有多種型別,以後會學習到),繫結後,就相當於在 array_buffer 這個目標上,當前**的工作物件是新建的緩衝區物件。

// 繫結緩衝區物件到  array_buffer  目標上

context.bindbuffer(context.array_buffer, vertexbuffer);

4、為了把頂點資料填充到緩衝區物件中,我們把頂點資料開放給 array_buffer 目標**的物件使用(這時**物件就是新建的緩衝區物件),執行完之後,緩衝區物件就有頂點資料了。

// 把頂點資料開放給  array_buffer  目標使用,間接把資料注入緩衝區物件中

context.bufferdata(context.array_buffer, vertices, context.static_draw);

5、到此,緩衝區物件有資料了,而且有多個頂點資料,接著,我們獲取頂點著色器中的頂點變數:a_position

// 獲取頂點著色器中頂點的位置變數

var a_position = context.getattriblocation(context.program, 'a_position');

6、我們的目的是要 a_position 能夠動態從緩衝區物件中獲取資料,所以我們還要設定獲取的規則,比如,資料的型別,每個頂點占用多少個變數,這樣才會正確處理緩衝區中的數值而不會亂來。

//  設定變數獲取資料規則

//  第二個引數2表示每個頂點只有兩個資料

//  後面兩個引數用於控制陣列包括多種資料內容的情況,在本例中都為0,後面用到再詳細解釋

context.vertexattribpointer(a_position, 2, context.float, false, 0, 0);

7、最後一步,當然是允許我們的頂點變數能夠從緩衝區物件中取數了,使用enablevertexattribarray方法表示變數將從 array_buffer目標**的緩衝區中獲取資料。

context.enablevertexattribarray(a_position);

一切都準備好了,最後我們還是使用 drawarrays介面來畫圖。

// 畫n個點,本例中是3個點,第二個引數表示從索引為0的頂點開始繪製,繪製總數為n個

context.drawarrays(context.points, 0, n);

小結為了能夠利用drawarrays一次性繪製多個點,我們使用了緩衝區物件,同時使用了著色器變數,利用webgl提供的內建機制,在執行 context.drawarrays(context.points, 0, n);的時候,會迴圈n次呼叫我們的頂點著色器**,每一次執行頂點著色器**時,底層會自動根據我們之前的相關配置,動態從緩衝區中取頂點的資料,然後填充到a_position變數中。這下子,就算要畫1000個點,我們只需要定義好點的位置,就可以一下子繪製出來了,這便是緩衝區物件的魅力所在。

本篇內容需要好好消化一下,理解每獲取乙個頂點的資料,就會執行一次頂點著色器**至關重要,這是底層的乙個執行的機制,包括片元著色器也是一樣的,後面自定義顏色再細講。

原始碼您的瀏覽器不支援canvas,建議使用chrome瀏覽器

WebGL學習 緩衝區物件(四)

緩衝區物件 webgl提供的一種機制,可以一次性向著色器傳入多個頂點資料,我們可以一次性向緩衝器物件中填充大量的頂點資料,然後將這些資料儲存起來,供頂點著色器使用。初始化過程 建立緩衝器物件 gl.createbuffer 繫結緩衝器物件 gl.bindbuffer gl.array buffer,...

對緩衝區的理解

緩衝區 buffer 它是記憶體空間的一部分。也就是說,在記憶體空間中預留了一定的儲存空間,這些儲存空間用來緩衝輸入或輸出的資料,這部分預留的空間就叫做緩衝區,顯然緩衝區是具有一定大小的。緩衝區根據其對應的是輸入裝置還是輸出裝置,分為輸入緩衝區和輸出緩衝區。我們為什麼要引入緩衝區呢?高速裝置與低速裝...

WebGL之旅(二十)幀緩衝區物件FBO

前面都是將作為繪製圖形時的紋理對映,這一節來看看如何將渲染結果作為紋理,即動態生成紋理,貼在另乙個物體上。預設情況下,都是在螢幕提供幀緩衝區中繪製,如果要動態生成紋理,就需要另外新建乙個緩衝區物件,來代替預設的緩衝區,在其中進行離屏繪製。幀緩衝區物件包括 顏色關聯物件 可以是紋理物件或渲染緩衝區物件...