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
的屬性byteOffset
和byteLength
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屬性也能夠取得數組緩衝器;
getter
和setter
讀寫方法讀取和寫入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的名字叫作「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()
方法,該方法在繪圖期間會重複執行。
爲着色器傳遞數據的方式有兩種:
Attribute
和Uniform
。經過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,但默認是禁用的。