跳到主要內容
黯羽輕揚每天積累一點點

uniform 變量與片元著色器_WebGL 筆記 4

免費2015-12-24#JS#WebGL#WebGL uniform#webgl uniform变量#webgl设置颜色#webgl修改颜色

通過 attribute 變量向片元著色器中傳值,我們可以動態修改點的顏色

寫在前面

[上一篇筆記](/articles/在鼠標點擊處繪製一個點-webgl 筆記 3/) 中,我們通過 attribute 變量把鼠標點擊處的坐標信息傳遞到了頂點著色器,實現了點哪裡就在哪裡畫點的效果,但畫出來的點都是同一種顏色,如果想要動態設置點的顏色,就需要 uniform 變量了,因為attribute 變量只能用於頂點著色器,而顏色信息在片元著色器中

一、uniform 變量與 attribute 變量

uniform 與 attribute 類似,都是存儲限定符,主要的區別如下:

  • attribute

只能用於頂點著色器,用來表示逐頂點的數據(每個頂點中該值不一樣),比如前面用到的 attribute vec4 a_Position

  • uniform

可以用於頂點著色器,也可以用於片元著色器,用來表示一致的、不變的數據(每個頂點中該值都一樣),比如以後紋理映射中要用到的 uniform Sampler2D u_Sampler,還有我們即將使用的 uniform vec4 u_FragColor

P.S. uniform 與 attribute 還有其它區別,而且還有一個存儲限定符是 varying,這些內容都將在 GLSL ES 語法筆記中展開

二、設置點的顏色

和設置坐標類似,先聲明 uniform 變量接收顏色數據,再傳入色值,繪製出來即可

1. 片元著色器

著色的過程在片元著色器中,我們需要修改片元著色器源程序:

// 片元著色器源程序
//!!! 需要聲明浮點數精度,否則報錯 No precision specified for (float) 
var fsSrc = 'precision mediump float;' +
    'uniform vec4 u_FragColor;' +
    'void main() {' +
    'gl_FragColor = u_FragColor;' + // 設置顏色
'}';

用法和 attribute 變量完全一致,但需要特別注意的一點是:需要聲明浮點數精度,否則片元著色器會編譯失敗,並報如下錯誤:

ERROR: 0:1: '' : No precision specified for (float)

2. 設置顏色

類似於 gl.getAttribLocationgl.vertexAttrib3f,這裡用的是 gl.getUniformLocationgl.uniform4f

// 設置顏色
// 獲取 uniform 變量的存儲位置
var u_FragColor = gl.getUniformLocation(glUtil.program, 'u_FragColor');
var color = item.color;
// 把色值傳遞給 uniform 變量
gl.uniform4f(u_FragColor, color.r, color.g, color.b, color.a);

注意色值 rgba 都是 0.0~1.0 的,不在此區間的數值會被自動截斷為 0.0 或者 1.0,但不報錯

3. 繪製多個點

此處是直接循環 gl.drawArrays(gl.POINTS, 0, 1); 實現的,方法不科學但能實現效果,更科學的方法需要配合 buffer,這是下一篇筆記的內容,我們暫時還沒有解鎖

var arrPos = [];
webgl.addEventListener('click', function(e) {
    // 轉換坐標
    var x = (e.offsetX - webgl.width / 2) / (webgl.width / 2);
    var y = (webgl.height / 2 - e.offsetY) / (webgl.height / 2);
    console.log(x, y);
    // 隨機顏色
    var color = {
        r: Math.random(),
        g: Math.random(),
        b: Math.random(),
        a: Math.random()
    };
    // 記錄坐標、顏色
    arrPos.push({
        x: x,
        y: y,
        color: color
    });

    // 清空 canvas
    gl.clear(gl.COLOR_BUFFER_BIT);
    // 在記錄的所有坐標處繪製點
    arrPos.forEach(function(item) {
        // 2. 給 attribute 變量賦值
        // 獲取 attribute 變量的存儲位置
        var a_Position = gl.getAttribLocation(glUtil.program, 'a_Position');
        if (a_Position < 0) {
            console.log('Failed to get the storage location of a_Position');
            return;
        }
        // 把頂點位置傳遞給 attribute 變量
        gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);

        // 設置顏色
        // 獲取 uniform 變量的存儲位置
        var u_FragColor = gl.getUniformLocation(glUtil.program, 'u_FragColor');
        var color = item.color;
        // 把色值傳遞給 uniform 變量
        gl.uniform4f(u_FragColor, color.r, color.g, color.b, color.a);

        // 繪製點
        gl.drawArrays(gl.POINTS, 0, 1);
    });
});

沒有新的東西,只是一個粗暴的循環,唯一需要注意的問題是循環之前的 clear,上一篇筆記提到了這個問題:繪製完成後,顏色緩衝區會被重置,其中的內容會丟失。從效果上來看,clear 是在每次繪製之前都設置了黑色背景,如果沒有 clear,背景就會變成透明的(vec4(0.0, 0.0, 0.0, 0.0))

三、DEMO

包含上述代碼的完整的例子,請查看:http://www.ayqy.net//temp/webgl/uniform/index.html

四、總結

我們已經實現了點點點點出燦爛星空的效果,不考慮性能的話,這樣實現還是不錯的,因為下一篇筆記要介紹的 buffer 看起來更麻煩,但好處還是有的,有了 buffer 就可以一次傳遞一組數據,以後畫三角形,給三角形塗色、貼圖都會很方便

循環 draw a point 畢竟不是長久之計,總不能用一個一個的點拼三角形吧,何況還有 3D 模型,buffer 還是很有必要的

參考資料

  • 《WebGL 編程指南》

評論

暫無評論,快來發表你的看法吧

提交評論