WebGL 的出現(xiàn)使得在瀏覽器上面實(shí)現(xiàn)顯示 3D 圖像成為可能,WebGL 本質(zhì)上是基于光柵化的 API ,而不是基于 3D 的 API。
WebGL 只關(guān)注兩個(gè)方面,即投影矩陣的坐標(biāo)和投影矩陣的顏色。使用 WebGL 程序的任務(wù)就是實(shí)現(xiàn)具有投影矩陣坐標(biāo)和顏色的 WebGL 對象即可。可以使用“著色器”來完成上述任務(wù)。頂點(diǎn)著色器可以提供投影矩陣的坐標(biāo),片段著色器可以提供投影矩陣的顏色。
無論要實(shí)現(xiàn)的圖形尺寸有多大,其投影矩陣的坐標(biāo)的范圍始終是從 -1 到 1 。下面是一個(gè)關(guān)于實(shí)現(xiàn) WebGL 對象的一個(gè)簡單例子。
// 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);
下面是兩個(gè)著色器。
<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>
它將繪出一個(gè)綠色的長方形來填充整個(gè)畫板。
后面內(nèi)容還會(huì)更精彩,我們繼續(xù):-P
我們再次強(qiáng)調(diào)一下,無論畫板尺寸多大,投影矩陣坐標(biāo)的范圍只會(huì)在 -1 到 1 之間。從上面的例子中,我們可以看出我們只是將位置信息直接寫在了程序里。 因?yàn)槲恢眯畔⒁呀?jīng)在投影矩陣中,所以并沒有其他額外的工作要做。 如果想實(shí)現(xiàn) 3D 的效果,那么可以使用著色器來將 3D 轉(zhuǎn)換為投影矩陣,這是因?yàn)?nbsp;WebGL 是基于光柵的 API。
對于 2D 的圖像,也許會(huì)使用像素而不是投影矩陣來表述尺寸,那么這里我們就更改這里的著色器,使得我們實(shí)現(xiàn)的矩形可以以像素的方式來度量,下面是新的頂點(diǎ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);
上面例子矩陣位于底部邊框,下面我們讓它位于左上邊框:
修改代碼如下:
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
效果如下圖:
下面我們將上述關(guān)于矩陣的實(shí)現(xiàn)寫成函數(shù)以便可以以函數(shù)調(diào)用的方式來實(shí)現(xiàn)不同尺寸的矩陣。 然而,這里的顏色應(yīng)該是可變的。
首先,我們?yōu)槠沃髟O(shè)計(jì)一個(gè)關(guān)于顏色的輸入。
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
下面是實(shí)現(xiàn)繪畫 50 個(gè)尺寸和顏色均隨機(jī)的矩陣的代碼。
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);
}
下面就是實(shí)現(xiàn)出來的效果。
更多建議: