webgl 6.畫3個不同顏色的點

上節我們畫了 3 個紅色的點,點的顏色是寫死在 fragment shader 中。這節我們來畫 3 個不同顏色的點。

three colored points.png

首先現在除了頂點的坐標之外還要把每個頂點的顏色傳到 shader 中。前面說過有 2 種方法,一是可以定義 2 個數組,2 個 buffer object 分別來傳遞坐標和顏色,二是可以把每個頂點的坐標和顏色都放在 1 個數組中用 1 個 buffer object 傳過去然后再分開解析。兩種方法各有利弊,首先分開傳肯定沒有一塊傳效率高,而一塊傳的弊端是當你只想更新顏色而坐標不變時也必須更新整個數組,這個要看具體情況。用 2 個 buffer object 分開傳的情況留給大家自己練習,我們來看看一塊傳的情況。

// vertex shader
var VERTEX_SHADER_SOURCE =
    'attribute vec4 a_Position;\n' +
    'attribute vec4 a_Color;\n' +
    'varying vec4 v_Color;\n' +

    'void main() {\n' +
    '   gl_Position = a_Position;\n' +
    '   gl_PointSize = 10.0;\n' +
    '   v_Color = a_Color;\n' +
    '}\n';

// fragment shader
var FRAGMENT_SHADER_SOURCE =
    'precision mediump float;\n' +
    'varying vec4 v_Color;\n' +

    'void main() {\n' +
    '   gl_FragColor = v_Color;\n' +
    '}\n';
  • attribute vec4 a_Color

增加了一個 attribute 變量 a_Color 來接收顏色

但顏色是在 fragment shader 中計算的,怎么把 vertex shader 中的顏色傳遞到 fragment shader 中呢? 這就要用到 varying 變量, 當同一個 varying 變量 (類型和名字都一樣)同時定義在 vertex shader 和 fragment shader 中時, varying 變量的值會自動從 vertex shader 中傳遞到 fragment shader 中。

  • varying vec4 v_Color;
    v_Color = a_Color;

我們定義了 v_Color 這個 varying 變量,并賦值為 a_Color

來個直觀的圖,大家感受一下

varyingvariable.png

attribute 和 varying 變量都必須定位為全局變量, main 是入口函數。

  • precision mediump float

這是什么意思呢? precision 是精度修飾符,mediump (medium precision) 中等精度, 意思是 fragment shader 中所有的 float 都用中等精度表示。
fragment shader 中默認沒有為 float 指定精度,必須手動指定,主要是為了在有些情況下必須使用 highp (高精度) 或用 lowp (低精度) 來提高性能。
vertex shader 中數據默認都是 highp 的,因為這適用于大多數情況。

現在頂點中要增加顏色信息

var vertices = new Float32Array([
    0.0, 0.5, 1.0, 0.0, 0.0, // (x,y) (r,g,b)
    -0.5, -0.5, 0.0, 1.0, 0.0,
    0.5, -0.5, 0.0, 0.0, 1.0
]);

每個頂點前 2 位表示位置,后 3 位表示顏色

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');

// 每個元素的字節數, 這里是  float 類型, 所以是 4 個字節
var BYTES_PER_ELEMENT = vertices.BYTES_PER_ELEMENT;

// 屬性變量和數據關聯起來并指定解析方法
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 5 * BYTES_PER_ELEMENT, 0);
// enable the assignment to attribute variable
gl.enableVertexAttribArray(a_Position);

gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 5 * BYTES_PER_ELEMENT, 2 * BYTES_PER_ELEMENT);
gl.enableVertexAttribArray(a_Color);
  • vertices.BYTES_PER_ELEMEN

得到每個元素的字節數, float 類型是 4 個字節

  • gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 5 * BYTES_PER_ELEMENT, 0);

a_Position 我們傳了 2 個值, 元素類型是 float
倒數第二個參數表示各個坐標之間間隔的字節數,每個頂點的坐標之間間隔 5 * 4 = 20 個字節
最后一個參數表示第一個頂點坐標的起始位置,這里是 0

  • gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 5 * BYTES_PER_ELEMENT, 2 * BYTES_PER_ELEMENT);

a_Color 我們傳了 3 個值, 元素類型是 float
倒數第二個參數表示各個顏色之間間隔的字節數,每個頂點的顏色之間間隔 5 * 4 = 20 個字節
最后一個參數表示第一個頂點顏色的起始位置,這里是 2 * 4 = 8 個字節

頂點中除了位置、顏色還可以攜帶 norm (法向量)、texture coord (紋理坐標) 等,一般都是一塊傳過去后再分開解析。

最后 draw 不變還是畫 3 個頂點

gl.drawArrays(gl.POINTS, 0, 3);

練習:

  1. 用 2 個 buffer object 分別傳遞位置和顏色

查看源碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容