[WebGL入門]十四,繪製多邊形

來源:互聯網
上載者:User

標籤:webgl

註:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。



這是本次的demo的運行結果

繪製流程

這次終於該繪製多邊形了,之前的文章(十一,著色器的編譯和串連)中介紹了HTML,頂點著色器和片段著色器,這次看一下javascript從開始到最終的全部處理。
如果前兩篇文章介紹的內容完全理解的話,這次的內容也應該不難了。或許會有不容易理解的地方,不要著急。
首先,我先貼出所有代碼,然後在慢慢說明。

>script.js的全部代碼

onload = function(){    // canvas對象擷取    var c = document.getElementById(‘canvas‘);    c.width = 300;    c.height = 300;    // webgl的context擷取    var gl = c.getContext(‘webgl‘) || c.getContext(‘experimental-webgl‘);        // 設定canvas初始化的顏色    gl.clearColor(0.0, 0.0, 0.0, 1.0);        // 設定canvas初始化時候的深度    gl.clearDepth(1.0);        // canvas的初始化    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);        // 頂點著色器和片段著色器的產生    var v_shader = create_shader(‘vs‘);    var f_shader = create_shader(‘fs‘);        // 程式對象的產生和串連    var prg = create_program(v_shader, f_shader);        // attributeLocation的擷取    var attLocation = gl.getAttribLocation(prg, ‘position‘);        // attribute的元素數量(這次只使用 xyz ,所以是3)    var attStride = 3;        // 模型(頂點)資料    var vertex_position = [         0.0, 1.0, 0.0,         1.0, 0.0, 0.0,        -1.0, 0.0, 0.0    ];        // 產生VBO    var vbo = create_vbo(vertex_position);        // 綁定VBO    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);        // 設定attribute屬性有効    gl.enableVertexAttribArray(attLocation);        // 添加attribute屬性    gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);        // 使用minMatrix.js對矩陣的相關處理    // matIV對象產生    var m = new matIV();        // 各種矩陣的產生和初始化    var mMatrix = m.identity(m.create());    var vMatrix = m.identity(m.create());    var pMatrix = m.identity(m.create());    var mvpMatrix = m.identity(m.create());        // 視圖變換座標矩陣    m.lookAt([0.0, 1.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix);        // 投影座標變換矩陣    m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);        // 各矩陣想成,得到最終的座標變換矩陣    m.multiply(pMatrix, vMatrix, mvpMatrix);    m.multiply(mvpMatrix, mMatrix, mvpMatrix);        // uniformLocation的擷取    var uniLocation = gl.getUniformLocation(prg, ‘mvpMatrix‘);        // 向uniformLocation中傳入座標變換矩陣    gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);        // 繪製模型    gl.drawArrays(gl.TRIANGLES, 0, 3);        // context的重新整理    gl.flush();        // 產生著色器的函數function create_shader(id){    // 用來儲存著色器的變數    var shader;        // 根據id從HTML中擷取指定的script標籤    var scriptElement = document.getElementById(id);        // 如果指定的script標籤不存在,則返回    if(!scriptElement){return;}        // 判斷script標籤的type屬性    switch(scriptElement.type){                // 頂點著色器的時候        case ‘x-shader/x-vertex‘:            shader = gl.createShader(gl.VERTEX_SHADER);            break;                    // 片段著色器的時候        case ‘x-shader/x-fragment‘:            shader = gl.createShader(gl.FRAGMENT_SHADER);            break;        default :            return;    }        // 將標籤中的代碼分配給產生的著色器    gl.shaderSource(shader, scriptElement.text);        // 編譯著色器    gl.compileShader(shader);        // 判斷一下著色器是否編譯成功    if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){                // 編譯成功,則返回著色器        return shader;    }else{                // 編譯失敗,彈出錯誤訊息        alert(gl.getShaderInfoLog(shader));    }}        // 程式對象的產生和著色器串連的函數function create_program(vs, fs){    // 程式對象的產生    var program = gl.createProgram();        // 向程式對象裡分配著色器    gl.attachShader(program, vs);    gl.attachShader(program, fs);        // 將著色器串連    gl.linkProgram(program);        // 判斷著色器的串連是否成功    if(gl.getProgramParameter(program, gl.LINK_STATUS)){            // 成功的話,將程式對象設定為有效        gl.useProgram(program);                // 返回程式對象        return program;    }else{                // 如果失敗,彈出錯誤資訊        alert(gl.getProgramInfoLog(program));    }}        // 產生VBO的函數function create_vbo(data){    // 產生緩衝對象    var vbo = gl.createBuffer();        // 綁定緩衝    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);        // 向緩衝中寫入資料    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);        // 將綁定的緩衝設為無效    gl.bindBuffer(gl.ARRAY_BUFFER, null);        // 返回產生的VBO    return vbo;}};

初始化處理

那麼,從上到下按順序來看一下吧。
首先,前提是在頁面載入的同時執行script.js中的所有代碼,所以,將代碼全都寫進了onload函數中。之後,擷取canvas對象開始處理。

>canvas的擷取和初始化如下

    // canvas對象擷取    var c = document.getElementById(‘canvas‘);    c.width = 300;    c.height = 300;    // webgl的context擷取    var gl = c.getContext(‘webgl‘) || c.getContext(‘experimental-webgl‘);        // 設定canvas初始化的顏色    gl.clearColor(0.0, 0.0, 0.0, 1.0);        // 設定canvas初始化時候的深度    gl.clearDepth(1.0);        // canvas的初始化    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
最先開始做的是擷取canvas對象,並設定canvas的大小為寬300px,高300px。然後擷取WebGL的context,以及設定清除畫面所使用的顏色。
接著是設定清除的深度。使用clearDepth函數可以設定清除畫面的時候的深度,以前的例子中只是對顏色進行了初始化,所以只使用了clearColor函數,其實處理三維空間的時候,深度相關的情報也需要清除,所以就用到了clearDepth函數。
同樣,clear函數中的參數也做了相應的變化,不光是顏色,還包含深度,所以增加了gl.DEPTH_BUFFER_BIT常量。


著色器和程式對象的產生

產生頂點著色器和片段著色器,並使用程式對象進行串連。在以前的文章(著色器的編譯和串連)中進行過詳細的說明,可以參考一下。

>著色器和程式對象相關的處理

    // 頂點著色器和片段著色器的產生    var v_shader = create_shader(‘vs‘);    var f_shader = create_shader(‘fs‘);        // 程式對象的產生和串連    var prg = create_program(v_shader, f_shader);        // attributeLocation的擷取    var attLocation = gl.getAttribLocation(prg, ‘position‘);        // attribute的元素數量(這次只使用 xyz ,所以是3)    var attStride = 3;
注意這裡出現的函數,並不是WebGL中內建的函數,而是自己寫的。具體點就是說create_shader和create_program都是自己寫的函數。
create_shader函數的參數是HTML中的id字串,從script標籤中擷取到著色器代碼,產生著色器對象並返回。上面的例子中,是根據id是vs和fs的這兩個script標籤中的內容,來產生頂點著色器和片段著色器。
著色器產生後,作為參數傳給create_program函數,傳回值就是程式對象,然後用變數儲存起來。在create_program函數中,產生程式對象,並將著色器進行串連。
接著,定義了兩個變數,attLocation和attStride,用來儲存在後面向頂點著色器中傳入資料時的必要的內容。
話說回來,看一下以前的文章中的代碼中是如何使用產生的著色器的,然後就明白為什麼要定義這兩個變數了吧。

>使用頂點著色器的代碼

attribute vec3 position;uniform   mat4 mvpMatrix;void main(void){    gl_Position = mvpMatrix * vec4(position, 1.0);}
這次使用著色器的時候,只使用了一個attribute變數,當然就是position了,這個position變數定義成了vec3,說明是擁有3各元素的向量。
這裡的重點就是[利用一個叫做position的attribute變數]和[這個變數是vec3類型]這兩點內容。其實利用attribute變數向著色器中傳遞資料的時候,必要的兩個情報是這個資料是第幾個attribute變數,以及這個變數由幾個元素組成的。
也就是說,變數attLocation是為了儲存這個資料是第幾個,變數attStride是為了儲存這個資料是由幾個元素組成的。
WebGL的context中的getAttribLocation函數的兩個參數,第一個參數是程式對象,第二個參數是想要擷取的attribute變數的變數名。傳回值是數值型,就是向頂點著色器傳遞資料時的序號。就是告訴我們是第幾個變數。這個數值後面會使用到。
變數attStride表示頂點著色器中的attribute變數position是一個有三個元素的vec3類型的變數。這個後面也會用到。


頂點緩衝的產生和通知

繼續吧,接著是定義模型資料,並產生VBO,然後為了將VBO傳給頂點著色器,進行綁定並傳入資料。

>VBO產生的相關處理

    // 模型(頂點)資料    var vertex_position = [         0.0, 1.0, 0.0,         1.0, 0.0, 0.0,        -1.0, 0.0, 0.0    ];        // 產生VBO    var vbo = create_vbo(vertex_position);        // 綁定VBO    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);        // 設定attribute屬性有効    gl.enableVertexAttribArray(attLocation);        // 添加attribute屬性    gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);
以前的文章(十二,模型資料和頂點屬性)中也有過詳細說明,將頂點資料定義為簡單的數組,然後根據這個數組資料,用自訂函數create_vbo來產生頂點緩衝(VBO)。
為了將頂點緩衝和頂點著色器中的attribute變數聯絡起來,首先要向WebGL中綁定VBO。然後使用剛才擷取到的attribute屬性的序號,將這個attribute屬性設定為有效。使用WebGL的函數enableVertexAttribArray可以讓指定屬性變為有效。
接著,使用WebGL函數vertexAttribPointer向著色器中寫入資料。剛才定義的兩個變數attLocation和attStride,在這裡也用到了。vertexAttribPointer函數的第一個參數是attribute變數的序號,第二個參數是元素數,第三個參數是指定了資料類型的內建常量。gl.FLOAT是一個表示浮點型的常量。第四~第六各參數基本上是不怎麼變的,根據記憶體有時候會傳入其他的內容。
需要注意的是,執行vertexAttribPointer的時候,VBO對象必須先進行綁定,哪個VBO以及和它關聯的attribute屬性是必須的,所以不要忘了先將VBO跟WebGL進行綁定。


座標變換矩陣的產生和通知

接著,是準備渲染用的座標變換矩陣。這裡使用了本網站製作的矩陣計算的庫minMatrix.js,minMatrix.js的基本的使用方法在上一篇文章中(minMatrix.js和座標變換矩陣)已經介紹過了。
>座標變換矩陣的產生以及相關的處理

    // 使用minMatrix.js對矩陣的相關處理    // matIV對象產生    var m = new matIV();        // 各種矩陣的產生和初始化    var mMatrix = m.identity(m.create());    var vMatrix = m.identity(m.create());    var pMatrix = m.identity(m.create());    var mvpMatrix = m.identity(m.create());        // 視圖變換座標矩陣    m.lookAt([0.0, 1.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix);        // 投影座標變換矩陣    m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);        // 各矩陣想成,得到最終的座標變換矩陣    m.multiply(pMatrix, vMatrix, mvpMatrix);    m.multiply(mvpMatrix, mMatrix, mvpMatrix);        // uniformLocation的擷取    var uniLocation = gl.getUniformLocation(prg, ‘mvpMatrix‘);        // 向uniformLocation中傳入座標變換矩陣    gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
這次,模型變換矩陣初始化之後沒有做任何處理就直接使用了,當然,並不是說不能操作模型變換矩陣,現在就先這麼使用了。
視圖變換矩陣使用minMatrix.js中定義的matIV.lookAt函數可以產生,向上面這樣,是將三維空間中的鏡頭放在了從原點開始向上移動1.0,向後移動3.0的地方,原點是參考點,鏡頭的方向指向Y軸方向。
投影變換矩陣是使用matIV.perspective產生的,視角是90度,螢幕比例為canvas的大小比例,然後分別指定了近截面和遠截面。
然後將模型,視圖,投影的各個變換矩陣相乘,得到最終的座標變換矩陣mvpMatrix,然後通知WebGL就可以了。
向WebGL中傳入uniform變數的時候,和attribute變數一樣,首先擷取變數的序號,使用WebGL的getUniformLocation函數,傳入程式對象和變數的名字,就可以得到uniform變數的序號了。
得到了序號之後,向頂點著色器中傳遞資料,這時候使用WebGL的uniformMatrix4fv函數,第一個參數是uniform變數的序號,第二個參數是矩陣是否進行轉置(true的話,有時候程式會崩潰),第三個參數是實際的座標變換矩陣。

>>uniform系列函數相關
這次出現的uniformMatrix4fv只是uniform系列函數的一個,uniform系列函數有很多種,主要分為以下幾大類。


uniform 系列
uniform系列有uniform1 ~ uniform4,分別在向頂點著色器中傳入一個到四各元素時使用。根據傳給頂點著色器的資料的類型為整型或者浮點型,數位後面會加上i(int)或者f(float)等小寫字母。比如,傳給著色器的資料是有兩個元素的浮點型資料的時候,使用uniform2f。
樣本 : gl.uniform2f(uniformLocation, date1, data2);


uniform v 系列
這一系列,基本上和上面的uniform系列沒有太大差別,傳入的資料是數組的時候使用。和uniform系列一樣,有1 ~ 4、資料類型也是在後面添加i或者f。
樣本 : gl.uniform3iv(uniformLocation, Array);


uniformMatrix 系列
看名字就應該知道了,這個系列是在矩陣的時候使用。當然,矩陣不可能出現1,所以只有2 ~ 4。而且,矩陣中基本上只使用浮點型小數、所以,資料類型也不存在i,資料就是以使用數組為前提的,所以後面加不加v都是沒有區別的。
樣本 : gl.uniformMatrix4fv(uniformLocation, false, Matrix);


模型的繪製和context的重新整理

著色器,頂點資料,座標變換矩陣等,各種準備工作都完工了,終於該寫繪製命令了。

>繪製命令和重新整理

// 繪製模型gl.drawArrays(gl.TRIANGLES, 0, 3);// context的重新整理gl.flush();
執行WebGL的drawArrays函數的話,模型就被繪製到了緩衝中了,這裡之所以說是[緩衝中],是因為當執行drawArrays函數的時候,還沒有把多邊形繪製到畫面上。
要想把模型繪製到畫面上,必須執行WebGL的flush函數,這樣才能把結果反映到畫面上。

這裡出現的drawArrays函數,第一個參數是指定如何使用頂點進行繪圖的一個常量,第二個參數是從第幾個頂點開始使用,第三個參數是繪製幾個頂點。

這一次,使用的是gl.TRAIANGLES,所以頂點被當成了純粹的三角形多邊形,利用三個頂點進行了繪製。


總結

這次的文章有點太長了,貼出的代碼量有點多,可能有人會吃了一驚吧。

其實這次的代碼繪製的只是一個簡單的三角形。只是這樣,卻寫了這麼長的代碼,所以才說3D開發是比較難的。

但是,個人認為,即使這樣,和DirectX相比較的話,已經相當簡單,簡練了。

單從開發環境上來說,不需要特別的開發環境,WebGL這一點已經很輕鬆了吧。理解了本次的內容的話,只需要稍微慢慢的調整一下,就可以實現很多效果。這以後的內容,都是以這次的內容為基礎的,所以必須要好好的理解一下。

本文章最後,給出本文demo的連結,如果瀏覽器支援的話,直接看demo比較直觀吧。


那麼,下次給多邊形進行著色,敬請期待。

渲染三角形的demo

http://wgld.org/s/sample_002/


轉載請註明:轉自lufy_legend的部落格http://blog.csdn.net/lufy_legend

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.