JavaScript Canvas——「WebGL」的注意要點

OpenGl:www.opengl.orgweb

WebGL:www.learningwebgl.comcanvas

WebGL是針對Canvas的3D上下文;OpenGL等是3D圖形語言;數組

類型化數組

類型化數組也是數組,只不過其元素被設置爲特定類型的值。瀏覽器

數組緩衝器ArrayBuffer類型和byteLength屬性

類型化數組的核心就是一個名爲函數

  • ArrayBuffer的類型。每一個ArrayBuffer對象表示的只是內存中指定的字節數,但不會指定這些字節用於保存什麼類型的數據。經過ArrayBuffer能作的,就是爲了未來使用而分配必定數量的字節。性能

如:webgl

var buffer = new ArrayBuffer(20); //在內存中分配20B

屬性ui

  • byteLength 返回它包含的字節數調試

如:code

var buffer = new ArrayBuffer(20);
console.log(buffer.byteLength); //20

數組緩衝器視圖

DataView數組緩衝器視圖

使用ArrayBuffer(數組緩衝器類型)的一種特別的方式就是用它來建立數組緩衝器視圖。其中,最多見的視圖是

  • DataView,經過它能夠選擇ArrayBuffer中的一小段字節。爲此,可在建立DataView實例的時候傳入一個ArrayBuffer、一個可選的字節偏移量(從該字節開始選擇)和一個可選的要選擇的字節數。

如:

var view = new DataView(buffer); //新的視圖
var view = new DataView(buffer, 6); //開始於字節6的新視圖
var view = new DataView(buffer, 6, 9); //開始於字節6,結束於字節9的新視圖

DataView的屬性byteOffsetbyteLength

DataView對象會把字節偏移量以及字符長度信息保存在

  • byteOffset

  • byteLength

兩個屬性中:

var view = new DataView(buffer, 6, 9); //開始於字節6,結束於字節9的新視圖
console.log(view.byteOffset); //6 字節偏移量爲6
console.log(view.byteLength); //9 字節長度爲9

buffer屬性也能夠取得數組緩衝器;

gettersetter讀寫方法

讀取和寫入DataView的時候,要根據實際操做的數據類型,選擇相應的

  • getter

  • setter

以下,列出了DataView支持的數據類型以及相應的讀寫方法:

getter:

  • getInt8(byteOffset) 方法: 在相對於視圖開始處的指定字節偏移量位置處獲取 Int8 值。

  • getUint8(byteOffset) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處獲取 Uint8 值。

  • getInt16(byteOffset,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處獲取 Int16 值。

  • getUint16(byteOffset,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處獲取 Uint16 值。

  • getInt32(byteOffset,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處獲取 Int32 值。

  • getUint32(byteOffset,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處獲取 Uint32 值。

  • getFloat32(byteOffset,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處獲取 Float32 值。

  • getFloat64(byteOffset,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處獲取 Float64 值。

setter:

  • setInt8(byteOffset,value) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Int8 值。

  • setUint8(byteOffset,value) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Uint8 值。

  • setInt16(byteOffset,value,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Int16 值。

  • setUint16(byteOffset,value,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Uint16 值。

  • setInt32(byteOffset,value,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Int32 值。

  • setUint32(byteOffset,value,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Uint32 值。

  • setFloat32(byteOffset,value,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Float32 值。

  • setFloat64(byteOffset,value,littleEndian) 方法 (DataView): 在相對於視圖開始處的指定字節偏移量位置處存儲 Float64 值。

如:

var buffer = new ArrayBuffer(20);
var view = new DataView(buffer);
view.setUint16(0,25); //0000000000001001
var value = view.getUint8(0); //00000000
console.log(value); //0

類型化視圖在讀寫數組緩衝器中更加便利:

類型化視圖(類型化數組)

類型化視圖通常也被稱爲類型化數組,由於它們除了元素必須是某種特定的數據類型外,與常規的數組無異。類型化視圖也分幾種,並且它們都繼承了DataView。

  • Int8Array:表示8爲二補整數。

  • Uint8Array:表示8位無符號整數。

  • Int16Array:表示16位二補整數。

  • Uint16Array:表示16位無符號整數。

  • Int32Array:表示32爲二補整數。

  • Uint32Array:表示32位無符號整數。

  • Float32Array:表示32位IEEE浮點值。

  • Float64Array:表示64位IEEE浮點值。

須要三個參數,只有第一個是必須的:ArrayBuffer對象、字節偏移量、要包含的字節數,如:

var buffer = new ArrayBuffer(20);
var int8s = new Int8Array(buffer);

注意:20B的ArrayBuffer能夠保存20個Int8Array或Uint8Array,或者10個Int16Array或Uint16Array,或者5個Int32Array或Uint32Array或Float32Array,或者2個Float64Array。

var buffer = new ArrayBuffer(20);
var int8s = new Int8Array(buffer); //建立一個新數組,使用整個緩衝器
var int16s = new Int16Array(buffer, 9); //只使用從字節9開始的緩衝器
var uint16s = new Uint16Array(buffer, 9, 10); //只使用從字節9到字節10的緩衝器

可以指定緩衝器中可用的字節段,意味着能在同一個緩衝器中保存不一樣類型的數值,以下面的代碼就是在緩衝器的開頭保存8位整數,而在其餘字節中保存16位整數:

var buffer = new ArrayBuffer(30); //緩衝器中有30個字節
var int8s = new Int8Array(buffer, 0, 10); //前面10個字節存儲10個8位整數
var int16s = new Int16Array(buffer, 10, 10); //後面還有20個字節,2個字節存儲1個16位整數,因此只能存儲10個

另外,每一個視圖構造函數都有一個名爲

  • BYTES_PER_ELEMENT

表示類型化數組的每一個元素須要多少字節:

console.log(Float64Array.BYTES_PER_ELEMENT) //8

這樣就能夠利用這個屬性來輔助初始化:

var buffer = new ArrayBuffer(20);
var int8s = new Int8Array(buffer, 0, 10 * Int8Array.BYTES_PER_ELEMENT);
var int16s = new Int16Array(buffer, int8s.byteOffset + int8s.byteLength, (10 / Int16Array.BYTES_PER_ELEMENT));

另外,還能夠不用首先建立ArrayBuffer對象,只要傳入但願數組保存的元素數,相應的構造函數就能夠自動建立一個包含足夠字節數的ArrayBuffer對象:

var int16s = new Int16Array(10); //建立一個數組保存10個16位整數(10字節)
var int32s = new Int32Array(1); //建立一個數組保存1個32位整數(4字節)

另外還能夠把常規數組轉換爲類型化視圖:

var int8s = new Int8Array([1,2,3,4]);
var view = new DataView(int8s.buffer);
console.log(int8s.toString()); //1234
console.log(view.byteLength); //4

對類型化視圖的迭代:

for (var i = 0; i < int8s.length; i++) {
    console.log(int8s[i]);
};

也可使用方括號語法爲類型化視圖的元素賦值:

var uint16s = new Uint16Array(10);
uint16s[0] = 65537;
console.log(uint16s[0]); //1

另外能夠經過

  • subarray()方法基於底層數組緩衝器的子集建立一個新視圖,接收兩個參數:開始元素的索引,可選的結束元素的索引:

如:

var uint16s = new Uint16Array(10),
    sub = uint16s.subarray(2, 5);

WebGL上下文

目前,在支持的瀏覽器中,WebGL的名字叫作「experimental-webgl」,這是由於WebGL規範仍然未制定完成。制定完成後,這個上下文的名字就會變成簡單的「webgl」。若是瀏覽器不支持WebGL,那麼取得該上下文時會返回null。

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    var gl = drawing.getContext("experimental-webgl");
    if (gl) {
        //[...]
    }
}

經過給getContext()傳遞第二個參數,能夠爲WebGL上下文設置一些選項。這個參數自己是一個對象,能夠包含下列屬性:

* `alpha`:值爲true,表示爲上下文建立一個Alpha通道緩衝區;默認值爲true;
* `depth`:值爲true,表示可使用16位深緩衝區;默認值爲true;
* `stencil`:值爲true,表示可使用8位模板緩衝區;默認值爲false;
* `antialias`:值爲true,表示將使用默認機制執行抗鋸齒操做;默認值爲true。
* `premultipliedAlpha`:值爲true,表示繪圖緩衝區有預乘Alpha值;默認爲true;
* `preserveDrawingBuffer`:值爲true;表示在繪圖完成後保留繪圖緩衝區;默認值爲false。

傳遞這個選項對象的方式以下:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    var gl = drawing.getContext("experimental-webgl", {
        alpha: false
    });
    if (gl) {
        //[...]
    }
}

大多數狀況下不用開啓,由於可能影響到性能,並且默認值通常都能知足咱們需求。

若是getContext()沒法建立WebGL上下文,瀏覽器可能會報錯。因此應該把它封裝到try-catch塊中:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    try {
        var gl = drawing.getContext("experimental-webgl");
    } catch (e) {}
    if (gl) {
        //[...]
    }
}

常量

在WebGL中,保存在上下文對象中的這些常量都沒有GL_前綴。

方法命名

方法名的後綴會包含參數個數(1到4),和接收的數據類型(f爲浮點數,i爲整數),如:gl.uniform4f()意味着要接收4個浮點數;另外還有不少方法接收數組參數而非一個個單獨的參數,這樣的方法中名字包含字母v,如:gl.uniform3iv()能夠接收一個包含3個值的整數數組。

準備繪圖

在實際操做WebGL上下文以前,通常都要使用某種實色清除canvas元素,爲繪圖作好準備。爲此,首先必須使用:

  • clearColor()方法來指定要使用的顏色值,這個方法接收4個參數:紅、綠、藍和透明度。每一個參數必須是一個0到1之間的數值,表示每種份量在最終顏色中的強度。

如:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    try {
        var gl = drawing.getContext("experimental-webgl");
    } catch (e) {}
    if (gl) {
        gl.clearColor(0,0,0,1); //把清理緩衝區的值設置爲黑色
        gl.clear(gl.COLOR_BUFFER_BIT); //調用clear方法,傳入參數gl.COLOR_BUFFER_BIT告訴WebGL使用以前定義的顏色來填充相應區域。
    }
}

視口與座標

開始繪圖以前,一般要先定義WebGL的視口(viewport)。默認狀況下,視口可使用整個canavs區域。要改變視口大小,能夠調用

  • viewport()方法並傳入4個參數:(視口相對於canvas元素的)x、y座標、寬度和高度。

視口座標的原點(0,0)在canvas元素的左下角,x軸和y軸的正方向分別是向右和向上,能夠定義爲(width-1,height-1)。

如:

var drawing = document.getElementById("drawing");
if (drawing.getContext) {
    try {
        var gl = drawing.getContext("experimental-webgl");
    } catch (e) {}
    if (gl) {
        gl.clearColor(0, 0, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);
        // gl.viewport(0, 0, drawing.width / 2, drawing.height / 2); //視口在畫布的左下角四分之一區域
        gl.viewport(drawing.width / 2, 0, drawing.width / 2, drawing.height / 2); //視口在畫布的右下角四分之一區域
    }
}

視口內部的座標系與定義視口的座標系也不同。在視口內部,座標原點(0,0)是視口的中心點,所以視口左下角座標爲(-1,-1),而右上角座標爲(1,1)。

緩衝區

頂點信息保存在JavaScript的類型化數組中,使用以前必須轉換到WebGL的緩衝區。要建立緩衝區,能夠調用

  • gl.createBuffer(),而後使用

  • gl.bindBuffer()綁定到WebGL上下文。這兩步作完之後,就能夠用數據來填充緩衝區了。

如:

var drawing = document.getElementById("drawing");
    if (drawing.getContext) {
        try {
            var gl = drawing.getContext("experimental-webgl");
        } catch (e) {}
        if (gl) {
            gl.clearColor(0, 0, 0, 1);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.viewport(drawing.width / 2, 0, drawing.width / 2, drawing.height / 2);
            var buffer = gl.createBuffer(); //建立緩衝區
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer); //綁定到上下文
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0.5, 1]), gl.STATIC_DRAW); //使用Float32Array中的數據初始化buffer
        }
    }
  • gl.bufferData()

最後一個參數主要有:

  • gl.STATIC_DRAW:數據只加載一次,在屢次繪圖中使用;

  • gl.STREAM_DRAW:數據只加載一次,在幾回繪圖中使用;

  • gl.DYNAMIC_DRAW:數據動態改變,在屢次繪圖中使用;

通常來講gl.STATIC_DRAW夠用了;

在包含緩衝區的頁面重載以前,緩衝區始終保留在內存中。若是你不想要某個緩衝區了,能夠直接調用

  • gl.deleteBuffer()釋放內存。

錯誤

JavaScript與WebGL之間的一個最大區別在於,WebGL操做通常不會拋出錯誤。爲了知道是否有錯誤發生,必須在調用某個可能出錯的方法後,手工調用

  • gl.getError()方法。這個方法返回一個表示錯誤類型的常量。

可能的錯誤常量以下:

  • gl.NO_ERROR:上一次操做沒有發生錯誤(值爲0)。

  • gl.INVALID_ENUM:應該給方法傳入WebGL常量,但卻傳錯了參數。

  • gl.INVALID_VALUE:在須要無符號數的地方傳入了負值。

  • gl.INVALID_OPERATION:在當前狀態下不能完成操做。

  • gl.OUT_OF_MEMORY:沒有足夠的內存完成操做。

  • gl.CONTEXT_LOST_WEBGL:因爲外部事件(如設備斷電)干擾丟失了當前WebGL的上下文。

若是發生了多個錯誤,須要反覆調用gl.getError()直到返回gl.NO_ERROR:

var errorCode = gl.getError();
while (errorCode) {
    console.log(errorCode);
    errorCode = gl.getError();
}

着色器

着色器(shader)是OpenGL 中的另外一個概念。WebGL中有兩種着色器:定點着色器和片斷(或像素)着色器。頂點着色器用於將3D頂點轉換爲須要渲染的2D點。片斷着色器用於準確計算要繪製的每一個像素的顏色。WebGL的着色器是使用GLSL(OpenGL Shading Language,OpenGL着色器)寫的,GLSL是一種與C和JavaScript徹底不一樣的語言。

編寫着色器

GLSL是一種類C語言,專門用於編寫OpenGL着色器。由於WebGL是OpenGL ES 2.0的實現,因此OpenGL中使用的着色器能夠直接在WebGL中使用。

每一個着色器都有一個

  • main()方法,該方法在繪圖期間會重複執行。

爲着色器傳遞數據的方式有兩種:

  • AttributeUniform。經過Attribute能夠向頂點着色器傳入頂點信息,經過Uniform能夠向任何着色器傳入常量值。

Attribute和Uniform在main()方法外部定義,分別使用關鍵字attribute和uniform。

如Attribute頂點着色器:

void main() {
    gl_Position = vec4(aVertexPosition, 0.0, 1.0);
}

又如Uniform片斷着色器:

void main() {
    gl_FragColor = uColor;
}

編寫着色器程序

瀏覽器不能理解GLSL程序,所以必須準備好字符串形式的GLSL程序,以便編譯並連接到着色器程序。

爲着色器傳入值

前面定義的着色器必須接收一個值才能工做。爲了給着色器傳入這個值,必須先找到要接收這個值的變量。

調試着色器和程序

與着色器的其餘操做同樣,着色器操做也可能會失敗,並且也是靜默失敗。若是你想找到着色器或程序執行中是否發生了錯誤,必須親自詢問WebGL上下文。

繪圖

WebGL只能繪製三種形狀:點、線和三角。其餘全部形狀都是由這三種基本形狀合成以後,再繪製到三維空間中的。執行繪圖操做要調用gl.drawArrays()或gl.drawElements()方法,前者用於數組緩衝區,後置用於元素數組緩衝區。

紋理

WebGL的紋理可使用DOM中的圖像。要建立一個新紋理,能夠調用gl.createTexture(),而後再將一副圖像綁定到該紋理。若是圖像還沒有加載到內存中,可能須要建立一個Image對象的實例,以便動態加載圖像。圖像加載完成以前,紋理不會初始化,所以,必須在load事件觸發後才能設置紋理。

讀取像素

與2D上下文相似,經過WebGL上下文也能讀取像素值。讀取像素值的方法readPixels()與OpenGL中的同名方法只有一點不一樣,即最後一個參數必須是類型化數組。像素信息是從幀緩衝區讀取的,而後保存在類型化數組中。readPixels()方法的參數有:x、y、寬度、高度、圖像格式、數據類型和類型化數組。前4個參數指定讀取哪一個區域中的像素。圖像格式參數幾乎老是gl.RGBA。數據類型用於指定保存在類型化數組中的數據類型,但有如下限制。

  • 若是類型是gl.UNSIGNED_BYTE,則類型化數組必須是Unit8Array。

  • 若是類型是gl.UNSIGNED_SHORT_5_6_五、gl.UNSIGNED_SHORT_4_4_4_四、或gl.UNSIGNED_SHORT_5_5_5_1,則類型化數組必須是Unit16Array。

15.3.3 支持

Firefox4+和Chrome都實現了WebGL API。Safari5.1也實現了WebGL,但默認是禁用的。
相關文章
相關標籤/搜索