1. 程式人生 > >[WebGL入門]二十六,紋理繪圖

[WebGL入門]二十六,紋理繪圖

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


WebGL與紋理

上次介紹了用點光源的光來進行補色著色的方法。
在片段著色器中對光進行計算,陰影,亮點等效果都非常的漂亮,3D場景的真實度大幅度提升。並且能和頂點顏色一起使用,理解了前面講解的內容之後,就應該能進行比較高質量的3D渲染了。
這一次,來看高階一點的紋理的使用。所謂紋理,簡單一點說,就是可以放到多邊形上的圖片資料,在WebGL中當然也可以使用。
WebGL和HTML不同,一般的圖片型別(gif,jpg,png等)是不可以直接使用的,另外,也可以把canvas轉換成紋理,總之,要變換一下方法來進行渲染。
這一次,先來看一下利用紋理的最基本的繪製方法。


WebGL中紋理的限制

上面說了,在HTML中使用的一般型別的圖片變換成紋理之後,就可以在WebGL中使用了。意思就是說要把網頁中使用的圖片資料變成WebGL中可以使用的形式。
但是,WebGL中的紋理需要注意一點,所使用的圖片資料的大小必須是2的階乘,橫豎的畫素長度大小必須是32x32,128x128等2的階乘的形式。

當然,做一些處理的話,不是2的階乘的圖片資料也是可以用的,但是基本上作為紋理使用的影象資料的大小必須是2的階乘。

另外,看一下普通的網頁就能感覺到,網頁上的圖片資料的讀取是要花一點時間的,在進行紋理轉換的話,必須是在圖片讀取完之後才行,這裡需要做一些特殊的處理,如果對javascript不太熟悉的話可能會無從下手,這個後面會說。


紋理的生成和使用

那麼,下面就開始說一說紋理使用的步驟吧。

紋理在WebGL中要使用紋理物件來處理,生成紋理物件需要使用createTexture函式。

>createTexture的使用例子

var tex = gl.createTexture();
這個函式沒有引數,只是單純的返回一個紋理物件,經過上面的程式碼,變數tex就是一個空的紋理物件了。
生成紋理物件之後,接著要把紋理物件和WebGL進行繫結。

大家想一想,之前在WebGL中使用快取物件的時候也是需要進行和WebGL的繫結處理,比如使用VBO的時候,這次也和快取一樣,要進行繫結處理。要對紋理資料進行操作的時候,首先必須先進行繫結,然後使用操作紋理的一些函式,被繫結的紋理物件才能適用這些處理。

把紋理資料和WebGL進行繫結的函式是bindTexture。

>bindTexture的使用例子

gl.bindTexture(gl.TEXTURE_2D, tex);
這個函式需要兩個引數。第一個引數是紋理的種類,繪製2D的影象型別的話,通常使用gl.TEXTURE_2D作為引數。第二個引數就是要繫結的紋理物件,這裡還是挺簡單的吧。
雖然將紋理物件和WebGL進行了繫結,但是還沒有把最核心的影象資料加進來,將影象資料和紋理進行連線的是texImage2D函式。

>texImage2D的使用例子

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
看了上面的程式碼,一定會有”這是什麼東東?“的感覺吧,這個函式一共接收六個引數,看起來複雜,其實挺簡單的。
第一個引數是bindTexture的時候也使用過的紋理的型別,這裡也使用gl.TEXTURE_2D就行了。第二個引數紋理對映的等級,暫時不用考慮,設定成0就行了。接著,第三個引數和第四個引數中都指定了gl.RGBA,現階段先不用考慮,直接這麼用就行了。同樣,第五個引數也是沒什麼特別的利用的話,先指定gl.UNSIGNED_BYTE就可以了。主要是,現階段第一個到第五個引數先像上面這樣設定就沒問題了,最重要的是第六個引數,這裡需要指定影象資料,這個第六個引數中的影象資料在這個時候分配給繫結的紋理。

圖片讀取時間的考慮

剛才也說了,使用texImage2D函式可以把圖片資料接分配給紋理,但是網頁中的圖片的讀取是要花一點時間的。

這裡需要注意的是,texImage2D函式被呼叫的時候,必須是圖片已經讀取完了之後,如果在圖片讀取完之前呼叫texImage2D,則無法正確的將圖片資料分配給紋理。。

這裡需要在圖片讀取完後的事件中來呼叫。

具體流程,就是先用javascript來生成圖片物件,圖片物件有onload事件可以監測到圖片讀取完成。在這裡把紋理相關的處理做完,最後給圖片物件指定圖片地址開始讀取圖片。

這裡比較重要的一點是,在圖片開始讀取之前,要先新增onload事件,在事件中新增紋理相關的處理,這樣,圖片讀取完成之後就可以自動進行紋理相關的這些處理了。

上面說的這些處理,寫成函式的話,就是下面這樣。

>紋理的生成函式

function create_texture(source){
    // イメージオブジェクトの生成
    var img = new Image();
    
    // データのオンロードをトリガーにする
    img.onload = function(){
        // テクスチャオブジェクトの生成
        var tex = gl.createTexture();
        
        // テクスチャをバインドする
        gl.bindTexture(gl.TEXTURE_2D, tex);
        
        // テクスチャへイメージを適用
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
        
        // ミップマップを生成
        gl.generateMipmap(gl.TEXTURE_2D);
        
        // テクスチャのバインドを無効化
        gl.bindTexture(gl.TEXTURE_2D, null);
        
        // 生成したテクスチャをグローバル変數に代入
        texture = tex;
    };
    
    // イメージオブジェクトのソースを指定
    img.src = source;
}
這個自定義函式create_texture接收一個圖片物件的圖片地址作為引數。函式中,首先生成圖片物件,在圖片資料讀取之前,先添加了onload事件,在事件中,有紋理的生成,繫結,以及分配圖片資料等處理。在函式中,texImage2D函式執行之後,應該會感覺有點奇怪吧,為了生成紋理對映,使用了generateMipmap函式。
紋理對映是一個提前準備一些不同大小的圖片資料的組織,不光是在WebGL中,在所有的3D程式設計中都有這個概念。紋理對映,準備一個紋理使用的場景,在紋理影象需要縮小顯示的時候能夠發揮很大的作用,因為縮小後的影象資料已經在內部提前準備好,然後進行適當的切換渲染,所以即使把影象縮的再小,也能渲染的很漂亮。

執行了generateMipmap函式之後,就能生成紋理映射了,這個函式的引數和bindTexture一樣,也是gl.TEXTURE_2D。

和VBO一樣,WebGL中同一時間只能繫結一個紋理,所以最後要解除繫結。在最終生成紋理物件的時候,使用了全域性變數,因為onload沒有辦法返回自己本身。上面的例子,變數texture必須是create_texture函式能夠參考到的空間內。

onload的對應結束之後,最後給圖片物件指定圖片地址,因為已經加了onload事件,圖片讀取完成之後會自動呼叫onload事件,執行生成紋理的程式碼。

紋理座標和頂點的屬性

好了,已經知道了紋理物件的生成的方法了,那麼重要的就是如何把紋理放到多邊形中了。

要把紋理放到多邊形中,在生成多邊形的時候,就需要包含將紋理如何放到多邊形中這樣的資訊。所以需要給頂點新增新的頂點屬性。

在前面的文章(九,頂點快取的基礎)中已經詳細介紹了,向頂點中新增資訊需要使用新的VBO。那麼這一次新新增的VBO中需要儲存頂點的紋理座標。紋理座標就是為了表示使用紋理的哪個座標。紋理座標使用的範圍是0 ~ 1,而且有橫豎兩個方向。所以,表示紋理座標的時候類似於(0.0, 0.0)這樣,需要兩個元素。(lufy:翻譯的有點繞嘴,看後面的使用部分就明白了。

而且,這裡有些奇怪的地方,一般,圖片資料的座標系是以左上為原點來考慮的,如下圖所示。


從左上角的原點,向右以及向下來對應X和Y的值的大小。

而WebGL中的紋理座標系則是像下面這樣。


座標系上下顛倒了一下。就是說紋理座標系中,左下是原點,縱方向上的數值越向上表示越大。但是,對照兩個圖看一下就知道了,指定紋理座標的時候也不需要考慮太多。為什麼呢?因為圖片也是上下翻轉的,所以和以左上角為原點的圖片一樣考慮就行,結果是一致的。

現在的階段,只需要知道紋理空間上座標系是上下翻轉的就可以了。*以後使用紋理進行一些特殊的處理的時候才需要詳細瞭解相關的知識。

javascript 的修正

接著,為了在程式中使用紋理,來修改一下程式碼吧。

這次沒有任何光照效果,渲染的只是帶有影象的多邊形而已。雖然用圓環體也可以,但是還需要處理一些細節部分,所以還從多邊形模型來開始介紹。

首先,準備模型的頂點資料,剛才也說了,為了儲存紋理座標,要新增新的頂點屬性,而因為不需要光照效果,所以法線等情報這次也不使用。

>頂點資料的準備

// attributeLocationを配列に取得
var attLocation = new Array();
attLocation[0] = gl.getAttribLocation(prg, 'position');
attLocation[1] = gl.getAttribLocation(prg, 'color');
attLocation[2] = gl.getAttribLocation(prg, 'textureCoord');

// attributeの要素數を配列に格納
var attStride = new Array();
attStride[0] = 3;
attStride[1] = 4;
attStride[2] = 2;

// 頂點の位置
var position = [
    -1.0,  1.0,  0.0,
     1.0,  1.0,  0.0,
    -1.0, -1.0,  0.0,
     1.0, -1.0,  0.0
];

// 頂點色
var color = [
    1.0, 1.0, 1.0, 1.0,
    1.0, 1.0, 1.0, 1.0,
    1.0, 1.0, 1.0, 1.0,
    1.0, 1.0, 1.0, 1.0
];

// テクスチャ座標
var textureCoord = [
    0.0, 0.0,
    1.0, 0.0,
    0.0, 1.0,
    1.0, 1.0
];

// 頂點インデックス
var index = [
    0, 1, 2,
    3, 2, 1
];

// VBOとIBOの生成
var vPosition     = create_vbo(position);
var vColor        = create_vbo(color);
var vTextureCoord = create_vbo(textureCoord);
var VBOList       = [vPosition, vColor, vTextureCoord];
var iIndex        = create_ibo(index);

// VBOとIBOの登録
set_attribute(VBOList, attLocation, attStride);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iIndex);
定義了一個四個頂點的四邊形。詳細看一下儲存頂點的位置的部分的話就明白了,是以四邊形的中心為原點,頂點順序和寫字母Z的順序一樣,頂點顏色定義成了不透明的白色。
紋理座標和之前說的一樣,定義了包含橫豎的兩個元素。

頂點資料是用一個數組來表示的,和之前一樣生成VBO和IBO。這和之前所作的是完全一樣的。這樣,頂點相關的處理,也就是說著色器內用attribute變數處理的資料的準備都已經ok了。

這次因為不進行光照處理,所以準備好座標變換矩陣就可以處理頂點了。逆矩陣和光源的位置等都是不需要的。

但是考慮一下uniform修飾符的變數的話,需要增加一個,uniform修飾符定義的變數是指全部頂點都進行一致處理的資料,所以,作為所有頂點都同樣被使用的紋理資料,就必須使用uniform變數來傳遞資料了。

>uniform相關處理

// uniformLocationを配列に取得
var uniLocation = new Array();
uniLocation[0]  = gl.getUniformLocation(prg, 'mvpMatrix');
uniLocation[1]  = gl.getUniformLocation(prg, 'texture');
這次使用的uniform變數有兩個,一個是為了處理座標變換矩陣,另一個是為了提交紋理資料。

設定紋理有效

紋理中有單位這個概念,是為了給紋理設定編號從而管理紋理的東西,預設是將0號的紋理單位設定為有效。紋理單位在處理多個紋理的時候可以發揮作用,這次只是使用一個紋理,所以就使用預設的0號的單位就可以了。

要將特定的紋理單位設定為有效使用的是activeTexture函式。

>紋理單位有效化

// 有効にするテクスチャユニットを指定
gl.activeTexture(gl.TEXTURE0);
這裡作為引數使用的gl.TEXTURE0常量,後面的0就是紋理單位的編號,如果將編號1的紋理設定為有效的話,就需要是gl.TEXTURE1。但是,沒有什麼特殊的理由的話,使用紋理單位應該按照從小到大的順序來使用。
>>紋理單位的最大值(上限值)
多個紋理同時使用的時候,紋理單位是必須用到的,這個最大的單位數是由執行環境決定的。因為執行WebGL的除了電腦,還有手機等,所以紋理單位能使用到多少個判斷起來是非常費勁的。
因為是受到硬體的效能的制約,所以使用之前先判斷一下,然後進行分別處理是可行的。查詢執行環境的可使用最大紋理單位數使用getParameter函式。
下面是例子
gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
向getParameter函式中傳入這個非常長的常量gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS就可以得到一個整數值,表示可以使用的最大文理單位數,如果返回值是10的話,那麼可以使用的紋理單位就是gl.TEXTURE0 ~ gl.TEXTURE9。

向著色器中傳入紋理資料

將相應的紋理單位設定為有效之後,接著就需要將紋理和WebGL進行繫結處理了,這個在將圖片資料轉換成紋理資料的時候也做過了,所以簡單如下。

>紋理和WebGL繫結

// テクスチャをバインドする
gl.bindTexture(gl.TEXTURE_2D, texture);
綁定了紋理情報要傳送給著色器,所以需要用uniform變數來處理,這裡還使用之前的uniformLocation,處理如下。

>將紋理資料傳給著色器

// uniform変數にテクスチャを登録
gl.uniform1i(uniLocation[1], 0);
這裡需要注意的是,和向著色器中傳入矩陣和向量不同,因為需要傳入紋理單位的編號。uniform1i函式是向著色器中傳入一個整數的時候使用的。第二個引數就是要向著色器中傳入的整數0。也就是說,這裡傳入的整數是和之前有效化的紋理單位相一致的。

著色器修改

接著,是著色器的修改了,首先是頂點著色器。

>頂點著色器程式碼

attribute vec3 position;
attribute vec4 color;
attribute vec2 textureCoord;
uniform   mat4 mvpMatrix;
varying   vec4 vColor;
varying   vec2 vTextureCoord;

void main(void){
    vColor        = color;
    vTextureCoord = textureCoord;
    gl_Position   = mvpMatrix * vec4(position, 1.0);
}
頂點著色器中,頂點的位置,頂點的顏色,還有頂點的紋理座標都是用attribute修飾符定義的。頂點的顏色和紋理座標沒有做任何處理,都是直接傳給片段著色器的。
頂點著色器相關的處理沒什麼難的地方,接著看片段著色器。

>片段著色器程式碼

precision mediump float;

uniform sampler2D texture;
varying vec4      vColor;
varying vec2      vTextureCoord;

void main(void){
    vec4 smpColor = texture2D(texture, vTextureCoord);
    gl_FragColor  = vColor * smpColor;
}
片段著色器使用uniform修飾符來接收紋理資料。注意這個sampler2D的變數型別,就是取樣的意思,先把它當作紋理資料考慮就行了。
另外,還使用了一個texture2D函式,這個函式有兩個引數,第一個引數是取樣型的紋理資料,第二個引數是表示紋理座標的vec型的資料。

這次的例子,頂點著色器中用attribute定義的頂點的紋理座標,在片段著色器中用varying變數來接收,然後在片段著色器一側將這個紋理座標,傳入到texture2D函式中。

這樣,用texture2D函式獲取到紋理的顏色資訊之後,再和頂點顏色(varying變數vColor)相乘,就得到最終的顏色了。


總結

紋理的使用方法,用了雙倍的時間來講解了,應該理解了吧。

紋理周邊的處理是非常冗長的,但是主要的就是紋理座標,紋理物件,以及為了處理這些資料的著色器的對應,紋理中單位這個概念,使用合理的操作可以處理多個紋理等。

雖然這次只是最基本的部分的封裝,但是變更點非常的多,所以最後會貼出全部程式碼(lufy:我就不貼了,大家直接用瀏覽器檢視就行了。),另外,最後給出了執行demo。

下次,介紹一下多個紋理的使用。

四邊形中使用紋理的demo

相關推薦

[WebGL入門]紋理繪圖

注:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。WebGL與紋理上次介紹了用點光源的光來進行補色著色的方法。在片段著色器

[WebGL入門]紋理

注:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。使用多個紋理上次介紹了WebGL中的紋理的使用方法。簡單的實現了將紋理貼

[WebGL入門]透明混色

混色上次介紹了紋理引數和它的設定方法,這次暫時不說紋理,來介紹一下混色。混合(blend)大家應該知道了,那麼混合什麼呢,WebGL中把顏色混合在一起就稱之為混色。跟遮擋剔除和深度測試等一樣,混色預設也是無效的,需要先把它設定為有效才能使用。但是,進行混色,把顏色混合在一起又

Java學習總計()——JavaScript正則表達式Js表單驗證,原生js+css頁面時鐘

text 先來 helloword 郵箱 用戶名 就是 lac round 外部 一.JavaScript正則表達式1.exec檢索字符串中指定的值,返回找到的值,並確定其位置2.test檢索字符串中指定的值,返回true或false3.正則表達式對象的創建:(1)方式一:

ElasticSearch最佳入門實踐()bulk批量增刪改

1、bulk語法 POST /_bulk { “delete”: { “_index”: “test_index”, “_type”: “test_type”, “_id”: “3” }} { “create”: { “_index”: “test_index”, “_typ

Effective_STL 學習筆記() 儘量使用 iterator 代替 const_iteratorreverse_iterator和const_reverse_iterator

  每個標準容器類都提供四種迭代器型別,對於container<T>而言: 1 iterator          // 的作用相當於T*, 2 const_iterator       // 相當於 const T*(也可 T const*) 3

教你ExtJS從入門到放棄——篇(示例23:DomHelper具類的方法:createHtml,createDom,append,insertHTML,overwrite,applyStyle)

為什麼要學這個工具類 說到這幾個方法之前,首先看看當引數為obj時,obj由哪些部分組成, tag:標籤名,比如input,div,ol,tr等等, children或者cn:子元素陣列,子元素也可以是obj或者單純的字串等 cls:樣式可以是字串或者物件形式

劍指offer----輸入用字母表示的列號編碼核心內容:進位制變十進位制

題目: 用A表示第一列,用B表示第二列。。。。用Z表示第26列,用AA表示第27列。。。請寫出一個函式,輸入用字母表示的列號編碼,輸出它是第幾列。 思路: 此問題最精彩之處,核心---二十六進位制變十進位制 function calColumn(){ // va

javaSE ()map集合遍歷(兩種方法)、輸入字元計算字元出現次數(用map實現)、HashMap巢狀HashMap

1、map集合遍歷: map集合沒有iterator方法,所以不能直接迭代 直接看下面的程式碼和第一行的註解(加了註釋之後變黑看不清了,所以前面沒加註釋) 1、map的第一種遍歷:遍歷map的所有值:method1() 獲取所有的鍵的集合:Set<K> keySet()

通證經濟大局觀():公司的前身寫在比特幣週年之際

十年前,幾代人迎來了最嚴重的經濟危機。此時,救世主誕生了。至少,它曾經給大家帶來了希望。2008年10月31日,一篇講述“比特幣——一個點對點的電子現金系統”的文章悄然出現在網路上。比特幣10週年:改變了一切,又似乎未曾改變任何。好了,進入我們今天的話題。 早期的“公司”

python 入門之 – 進位制運算(

二進位制是由 0 ~ 1 組成的 八進位制是由 0 ~ 7組成的 十進位制是由 0 ~ 9組成的 十六進位制是由 0 ~ 15 組成的,可是 9 後面的的 10 是用字母來代替 A~ F ,也就是 0 ~ F,用字母代替了數字,避免不再重複 之前剛接觸 python 的時候學了以下 二進

java基礎重點講解看了還不會找我(

day26授課目錄: ###26.01_網路程式設計(網路程式設計概述)(瞭解) * A:計算機網路     * 是指將地理位置不同的具有獨立功能的多臺計算機及其外部裝置,通過通訊線路連線起來,在網路作業系統,網路管理軟體及網路通訊協議的管理和協調下,實現資源共享和

VS2013/MFC程式設計入門(常用控制元件:圖片控制元件Picture Control)

本節主要講一種簡單實用的控制元件,圖片控制元件Picture Control。通過使用圖片控制元件我們可以在介面某個位置顯示圖片以美化介面。        圖片控制元件簡介        圖片控制元件和前面講到的靜態文字框都是靜態文字控制元件,因此兩者的使用方法有很多

C++學習總結()——RTTI型別檢查類指標型別轉換

#include<iostream> using namespace std; //rtti實時型別檢測 //成員變數的覆蓋,靜態變數也會覆蓋。 // class A { public:

[WebGL入門]十三反射光的光照效果

attribute vec3 position; attribute vec3 normal; attribute vec4 color; uniform mat4 mvpMatrix; uniform mat4 invMatrix; uniform vec3 lightDirection; un

組成原理中關於小數和整數關於八進制之間任意的轉換

位與 所在 十進制 0.10 進制轉換 數位 千萬 計數 介紹 一:我們首先介紹一下基數和位權這個概念: 二:十六、八、二進制轉換二.八、十六進制 三 十進制轉換二.八、十六進制 四 :任意進制數轉換為十進制教 一:我們首先介紹一下基數和位權這個概念: 1)基

Java 從入門到進階之路(

在之前的文章我們介紹了一下 Java 中的  集合框架中的Collection 的子介面 List,本章我們來看一下 Java 集合框架中的Collection 的子介面 Queue。 在之前我們講 List 和 Set 的時候可以通過下標的形式獲取想要的元素,在 Collection 中其實還有

Java進階專題() 將近2萬字的Dubbo原理解析徹底搞懂dubbo

# 前言 ​ 前面我們研究了RPC的原理,市面上有很多基於RPC思想實現的框架,比如有Dubbo。今天就從Dubbo的SPI機制、服務註冊與發現原始碼及網路通訊過程去深入剖析下Dubbo。 # Dubbo架構 ## 概述 Dubbo是阿里巴巴公司開源的一個高效能優秀的服務框架,使得應用可通過高效能的R

Spring入門第二

code message manage todo system framework auto manager isbn Spring中的事務管理 事務簡介 事務管理是企業級應用程序開發中必不可少的技術,用來確保數據的完整性和一致性。 事務就是一系列的動作,他們被當做一個單獨

Java經典編程題50道之

ava ret ner 判斷 大寫 scanner nbsp ring print 請輸入星期幾的第一個字母來判斷一下是星期幾,如果第一個字母一樣,則繼續判斷第二個字母。 public class Example26 { public static void mai