鍍金池/ 教程/ HTML/ WebGL 基本原理
WebGL 文本 HTML
WebGL 文本 Canvas 2D
WebGL 2D 圖像旋轉
WebGL 圖像處理(續(xù))
WebGL 2D 矩陣
WebGL 繪制多個東西
WebGL 圖像處理
WebGL 2D 圖像轉換
WebGL 3D 透視
WebGL 是如何工作的
WebGL 文本 紋理
WebGL 2D 圖像伸縮
WebGL 場景圖
WebGL 3D 攝像機
WebGL 文本 使用字符紋理
WebGL 正交 3D
WebGL 基本原理
WebGL - 更少的代碼,更多的樂趣
WebGL 著色器和 GLSL

WebGL 基本原理

WebGL 的出現(xiàn)使得在瀏覽器上面實時顯示 3D 圖像成為,WebGL 本質(zhì)上是基于光柵化的 API ,而不是基于 3D 的 API。

WebGL 只關注兩個方面,即投影矩陣的坐標和投影矩陣的顏色。使用 WebGL 程序的任務就是實現(xiàn)具有投影矩陣坐標和顏色的 WebGL 對象即可??梢允褂谩爸鳌眮硗瓿缮鲜鋈蝿铡m旤c著色器可以提供投影矩陣的坐標,片段著色器可以提供投影矩陣的顏色。

無論要實現(xiàn)的圖形尺寸有多大,其投影矩陣的坐標的范圍始終是從 -1 到 1 。下面是一個關于實現(xiàn) WebGL 對象的一個簡單例子。

// Get A WebGL context
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("experimental-webgl");
?
// setup a GLSL program
var program = createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
?
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
?
// Create a buffer and put a single clipspace rectangle in
// it (2 triangles)
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
 1.0, -1.0,
-1.0,  1.0,
-1.0,  1.0,
 1.0, -1.0,
 1.0,  1.0]),
gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
?
// draw
gl.drawArrays(gl.TRIANGLES, 0, 6);   

下面是兩個著色器。

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

void main() {
  gl_Position = vec4(a_position, 0, 1);
}
</script>

<script id="2d-fragment-shader" type="x-shader/x-fragment">
void main() {
  gl_FragColor = vec4(0, 1, 0, 1);  // green
}
</script>    

它將繪出一個綠色的長方形來填充整個畫板。

http://wiki.jikexueyuan.com/project/webgl/images/1.png" alt="" />

后面內(nèi)容還會更精彩,我們繼續(xù):-P

我們再次降調(diào)一下,無論畫板尺寸多大,投影矩陣坐標的范圍只會在 -1 到 1 之間。從上面的例子中,我們可以看出我們只是將位置信息直接寫在了程序里。 因為位置信息已經(jīng)在投影矩陣中,所以并沒有其他額外的工作要做。 如果想實現(xiàn) 3D 的效果,那么可以使用著色器來將 3D 轉換為投影矩陣,這是因為 WebGL 是基于光柵的 API。

對于 2D 的圖像,也許會使用像素而不是投影矩陣來表述尺寸,那么這里我們就更改這里的著色器,使得我們實現(xiàn)的矩形可以以像素的方式來度量,下面是新的頂點著色器。

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform vec2 u_resolution;

void main() {
   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = a_position / u_resolution;

   // convert from 0->1 to 0->2
   vec2 zeroToTwo = zeroToOne * 2.0;

   // convert from 0->2 to -1->+1 (clipspace)
   vec2 clipSpace = zeroToTwo - 1.0;

   gl_Position = vec4(clipSpace, 0, 1);
}
</script>    

下面我們將我們的數(shù)據(jù)從投影矩陣改為像素。

// set the resolution
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);

// setup a rectangle from 10,20 to 80,30 in pixels
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
10, 20,
80, 20,
10, 30,
10, 30,
80, 20,
80, 30]), gl.STATIC_DRAW);    

http://wiki.jikexueyuan.com/project/webgl/images/2.png" alt="" />

上面例子矩陣位于底部邊框,下面我們讓它位于左上邊框:

修改代碼如下:

   gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

效果如下圖:

http://wiki.jikexueyuan.com/project/webgl/images/3.png" alt="" />

下面我們將上述關于矩陣的實現(xiàn)寫成函數(shù)以便可以以函數(shù)調(diào)用的方式來實現(xiàn)不同尺寸的矩陣。 然而,這里的顏色應該是可變的。

首先,我們?yōu)槠沃髟O計一個關于顏色的輸入。

<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

uniform vec4 u_color;

void main() {
   gl_FragColor = u_color;
}
</script>    

下面是實現(xiàn)繪畫 50 個尺寸和顏色均隨機的矩陣的代碼。

var colorLocation = gl.getUniformLocation(program, "u_color");
  ...
  // Create a buffer
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.enableVertexAttribArray(positionLocation);
  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

  // draw 50 random rectangles in random colors
  for (var ii = 0; ii < 50; ++ii) {
// Setup a random rectangle
setRectangle(
gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300));

// Set a random color.
gl.uniform4f(colorLocation, Math.random(), Math.random(), Math.random(), 1);

// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
  }
}

// Returns a random integer from 0 to range - 1.
function randomInt(range) {
  return Math.floor(Math.random() * range);
}

// Fills the buffer with the values that define a rectangle.
function setRectangle(gl, x, y, width, height) {
  var x1 = x;
  var x2 = x + width;
  var y1 = y;
  var y2 = y + height;
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
 x1, y1,
 x2, y1,
 x1, y2,
 x1, y2,
 x2, y1,
 x2, y2]), gl.STATIC_DRAW);
}    

下面就是實現(xiàn)出來的效果。

http://wiki.jikexueyuan.com/project/webgl/images/3Mwhw40.png" alt="" />

到此可以看出 WebGL 實質(zhì)上是一種輕量級的 API。但是,它可以實現(xiàn)較為復雜的 3D 效果,其復雜性由程序定制。WebGL API 本身是 2D 的且相對比較簡單。