WebGL-學習筆記(三)

image.png

在利用緩沖區(qū)并在學會利用 mode繪制圖形動畫以后斟冕,繼續(xù)研究二維圖形的顏色渲染以及紋理操作

1. 顏色渲染

1.1 利用步進和偏移拆分緩沖區(qū)數(shù)據(jù)

利用緩沖區(qū)可以一次存儲點的多個信息,繼續(xù)利用這一特性,并在利用vertexAttribPointer()函數(shù)的時候酥馍,利用上strideoffset兩個參數(shù):

    function main() {
        const canvas = document.querySelector('#glCanvas');
        const gl = canvas.getContext('webgl');
        // 著色器程序
        const VSHADER_SOURCE = `
            attribute vec4 a_Position;
            attribute float a_PointSize;
            void main() {
                gl_Position = a_Position;
                gl_PointSize = a_PointSize;
            }
        `;
        const FSHADER_SOURCE = `
            precision mediump float;
            uniform vec4 u_FragColor;
            void main() {
                gl_FragColor = u_FragColor;
            }
        `
        let program = init(gl, VSHADER_SOURCE, FSHADER_SOURCE);
        let a_Position = gl.getAttribLocation(program, 'a_Position');
        if (a_Position < 0) {
            console.log('Cant find the position');
            return;
        }
        let a_PointSize = gl.getAttribLocation(program, 'a_PointSize');
        if (a_PointSize < 0) {
            console.log('Cant find the pointsize');
            return;
        }
        gl.vertexAttrib1f(a_PointSize, 10.0);
        initPoint(gl, a_Position, a_PointSize);
        let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
        gl.uniform4f(u_FragColor, 1.0, 0.0, 0.0, 1.0);

        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.POINTS, 0, 3);
    }
    function initPoint(gl, a_Position, a_PointSize) {
        // 將點的坐標和尺寸信息放入一個類型化數(shù)組中
        let pointData = new Float32Array([
            -0.5, -0.5, 10.0,
            0, 0.5, 20.0,
            0.5, -0.5, 30.0
        ]);
        // 利用類型化數(shù)組提供的內(nèi)部屬性摔认,獲取到元素的尺寸
        let FSIZE = pointData.BYTES_PER_ELEMENT;
        let buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, pointData, gl.STATIC_DRAW);
        // 設置第五個參數(shù)stride為3*FSIZE(每兩個點之間有三個元素,也就是每三個數(shù)據(jù)進行一次拆分)
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 3*FSIZE, 0);
        gl.enableVertexAttribArray(a_Position);
        // 設置第六個參數(shù)offset偏移龄减,將每組數(shù)據(jù)的第三個元素設置給a_PointSize屬性
        gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, 3*FSIZE, 2*FSIZE);
        gl.enableVertexAttribArray(a_PointSize);
    }

主要改變在于:

1.利用了類型化數(shù)組的BYTE_PER_ELEMENT屬性似忧,獲取了每個元素的大小

  1. 利用vertexAttribPointer()的第五個參數(shù)渣叛,指定兩個相鄰頂點間的字節(jié)數(shù)丈秩,或者理解為每個點的信息由多少個元素組成盯捌。
  2. 利用vertexAttribPointer()的第六個參數(shù),指定緩沖區(qū)對象中每次讀取時的偏移蘑秽。

當然饺著,要完成以上的操作箫攀,也可以將點的位置信息和尺寸信息進行分開放置:

function initPoint(gl, a_Position, a_PointSize) {
        // 將類型化數(shù)組增加點的尺寸的參數(shù)
        let pointData = new Float32Array([
            -0.5, -0.5,
            0, 0.5,
            0.5, -0.5
        ]);
        let pointSizeData = new Float32Array([10.0, 20.0, 30.0]);
        // 設置點的位置信息
        let buffer1 = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer1);
        gl.bufferData(gl.ARRAY_BUFFER, pointData, gl.STATIC_DRAW);
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(a_Position);
        // 設置點的尺寸信息
        let buffer2 = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer2);
        gl.bufferData(gl.ARRAY_BUFFER, pointSizeData, gl.STATIC_DRAW);
        gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(a_PointSize);
}

但是很明顯操作多余而且復雜了

1.2 利用varying變量進行插值操作

想要從Vertex ShaderFragment Shader進行數(shù)據(jù)傳遞,需要用到varying聲明的變量幼衰,為什么利用從Vertex ShaderFragment Shader傳遞數(shù)據(jù)靴跛?
由于Fragment Shader中是只能使用uniform變量的,unifrom信息是和頂點信息不相關(guān)聯(lián)的渡嚣,如果要單獨對某個頂點的顏色進行設置梢睛,是沒辦法的,所以為了給每個頂點設置顏色识椰,就要從Vertex Shader傳遞數(shù)據(jù)到Fragment Shader了绝葡。
如果繪制一個三角形,并將頂點顏色傳遞給Fragment Shader

// 修改著色器程序腹鹉,增加varying
const VSHADER_SOURCE = `
    attribute vec4 a_Position;
    attribute vec4 a_Color;
    varying vec4 v_Color;
    void main() {
        gl_Position = a_Position;
        v_Color = a_Color;
    }
`;
const FSHADER_SOURCE = `
     precision mediump float;
     varying vec4 v_Color;
     void main() {
        gl_FragColor = v_Color;
      }
`
// 修改點的數(shù)據(jù)信息加載
function initPoint(gl, a_Position, a_Color) {
    // 將點的坐標信息和顏色信息放到同一個類型化數(shù)組中
    let pointData = new Float32Array([
        -0.5, -0.5, 1.0, 0.0, 0.0,
        0, 0.5, 0.0, 1.0, 0.0,
        0.5, -0.5, 0.0, 0.0, 1.0
    ]);
    // 利用類型化數(shù)組提供的方法藏畅,獲取到元素的尺寸
    let FSIZE = pointData.BYTES_PER_ELEMENT;
    let buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, pointData, gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 5*FSIZE, 0);
    gl.enableVertexAttribArray(a_Position);
    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 5*FSIZE, 2*FSIZE);
    gl.enableVertexAttribArray(a_Color);
}

varying變量的作用其實不僅是進行數(shù)值的傳遞,如果將圖形繪制的mode修改為gl.TRANGLES功咒,可以發(fā)現(xiàn)繪制的三角形顏色是漸變的愉阎,造成這一結(jié)果是varying的作用。
繼續(xù)深入著色器過程力奋,Vertex ShaderFragment Shader要進行兩步操作:

  1. 圖形裝配:將Vertex Shader的頂點信息榜旦,根據(jù)drawArraymode配置在對應的幾何圖形頂點處
  2. 光柵化:將裝配好的圖形分割為一個一個的片元(像素點),因此片元的數(shù)目就是像素點

varying在賦值顏色給頂點的過程當中景殷,會自動計算從一個頂點到另一個頂點變化的插值章办,并將每一個片元染成對應計算后的顏色,所以才看到一個從頂點顏色漸變的三角形滨彻,這一過程也可以被稱為內(nèi)插藕届。

2. 紋理添加

除了使用單獨的顏色渲染外,也可以直接將圖片貼到幾何圖形的表面亭饵,該圖片也就是紋理休偶。圖片是由像素點組成的,每個像素點就是紋素辜羊,是使用了RGB/RGBA的編碼塊
WebGL的給圖形添加紋理很簡單踏兜,但是要解決三個問題

2.1 瀏覽器跨域

由于WebGL的圖片請求機制,所以如果直接請求本地圖片的時候會出現(xiàn)跨域問題八秃,為了使得開發(fā)和調(diào)試的方便碱妆,可以使用以下方式臨時關(guān)閉瀏覽器的跨域驗證,以Chrome為例:
Windows:

"C:\Program Files\Google\Chrome\Application\chrome.exe" --args --disable-web-security

MAC:

open -a "Google Chrome" --args --disable-web-security --user-data-dir

2.2 紋理坐標和圖片坐標

WebGL中的紋理坐標Y軸正方向是朝上昔驱,其中坐標的四個角為左下(0.0, 0.0)疹尾,右下(1.0, 0.0)右上(1.0, 1.0)左上(0.0, 1.0)纳本。
圖片坐標和WebGL坐標的Y軸方向是相反的窍蓝,正方向朝下,所以在進行紋理映射的時候繁成,需要將坐標軸進行反轉(zhuǎn)吓笙。

2.3 紋理添加

進行紋理映射最主要的有以下五步操作,以繪制一個矩形并貼圖為例:

2.3.1. 將頂點坐標和紋理坐標都加入到類型化數(shù)組中

// 每一行前兩個數(shù)據(jù)為頂點坐標巾腕,后兩個數(shù)據(jù)為紋理坐標
let pointData = new Float32Array([
    -0.5, 0.5, 0.0, 1.0,
    -0.5, -0.5, 0.0, 0.0,
    0.5, 0.5, 1.0, 1.0,
    0.5, -0.5, 1.0, 0.0
]);
2.3.2 創(chuàng)建紋理對象和圖片加載

// 創(chuàng)建image對象和紋理對象
let texture = gl.createTexture();
let image = new Image();
// 給image執(zhí)行onload方法的時候進行紋理渲染
image.onload = function() { initTexture() };
// 加載紋理圖片
image.src = 'xxx.png';

PS:圖片的加載過程是異步C婢Α!尊搬!

2.3.3 設置紋理

有6個主要的步驟侮穿,但是實際上去掉第2和6步也是可以運行的,目前對機制還不甚了解毁嗦,之后會持續(xù)關(guān)注

// 1. y軸反轉(zhuǎn)亲茅,因為圖像坐標系y軸向下,而webgl坐標系y軸向上
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
// 2. 開啟紋理單元(0號)狗准,一共有8個
gl.activeTexture(gl.TEXTURE0);
// 3. 綁定紋理對象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 4. 配置紋理參數(shù)(對于非2的冪次尺寸的圖片克锣,需要設置圖片拉伸方式)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// 如果圖片尺寸為2的冪次,則可以使用以下方式
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 5. 將圖像分配給紋理對象
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
// 6. 將0號紋理傳遞給著色器中的取樣器變量
gl.uniform1i(u_Sampler, 0);
2.3.4 修改著色器程序

// 增加紋理坐標的數(shù)據(jù)接收
const VSHADER_SOURCE = `
  attribute vec4 a_Position;
  attribute vec2 a_TexCoord;
  varying vec2 v_TexCoord;
  uniform mat4 u_Matrix;
  void main() {
    gl_Position = u_Matrix * a_Position;
    v_TexCoord = a_TexCoord;
  }
`;
// 給片元著色器增加 simlper2D的屬性以及紋理坐標數(shù)據(jù)
const FSHADER_SOURCE = `
  precision mediump float;
  uniform sampler2D u_Sampler;
  varying vec2 v_TexCoord;
  void main() {
    gl_FragColor = texture2D(u_Sampler, v_TexCoord);
  }
 `

PS:一定要注意其中關(guān)于圖片的選擇腔长,2.3.3 中關(guān)于配置紋理參數(shù)袭祟,一定要優(yōu)先確定圖片的長和寬是否為2的冪次,從而來決定使用哪種方案進行紋理的匹配捞附,否則將導致紋理解析錯誤:

It maybe non-power-of-2 and have incompatible texture filtering.

當然目前在測試的過程當中巾乳,還存在一個瀏覽器警告提示沒有解決

RENDER WARNING: there is no texture bound to the unit 0

目前雖然有警告,但是紋理渲染并沒有問題鸟召,而且根據(jù)描述來說胆绊,我已經(jīng)對紋理單元0進行了利用了,所以目前還不知道導致的原因欧募,之后如果解決會在評論區(qū)補充原因說明压状。

4. 總結(jié)

基本上來說二維圖形的繪制,渲染就大致這些內(nèi)容跟继,不過仍然存在很多細節(jié)需要自己去嘗試摸索种冬,接下來繼續(xù)=學習進入真正的WebGL發(fā)揮實力的三維圖形的領(lǐng)域!

5. 參考

《WebGL編程指南》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舔糖,一起剝皮案震驚了整個濱河市娱两,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌金吗,老刑警劉巖十兢,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趣竣,死亡現(xiàn)場離奇詭異,居然都是意外死亡纪挎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門跟匆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來异袄,“玉大人,你說我怎么就攤上這事玛臂】就桑” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵迹冤,是天一觀的道長讽营。 經(jīng)常有香客問我,道長泡徙,這世上最難降的妖魔是什么橱鹏? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮堪藐,結(jié)果婚禮上莉兰,老公的妹妹穿的比我還像新娘。我一直安慰自己礁竞,他們只是感情好糖荒,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著模捂,像睡著了一般捶朵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狂男,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天综看,我揣著相機與錄音,去河邊找鬼岖食。 笑死寓搬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的县耽。 我是一名探鬼主播句喷,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼兔毙!你這毒婦竟也來了唾琼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤澎剥,失蹤者是張志新(化名)和其女友劉穎锡溯,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡祭饭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年芜茵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倡蝙。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡九串,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寺鸥,到底是詐尸還是另有隱情猪钮,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布胆建,位于F島的核電站烤低,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏笆载。R本人自食惡果不足惜扑馁,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凉驻。 院中可真熱鬧檐蚜,春花似錦、人聲如沸沿侈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缀拭。三九已至咳短,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛛淋,已是汗流浹背咙好。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褐荷,地道東北人勾效。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像叛甫,于是被迫代替她去往敵國和親层宫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容