WebGL也是HTML5规范的一部分,从本质上讲它是一个增强型的图形绘制库,与HTML5的2D Canvas类似,只是WebGL是一个三维的绘图标准,并且同样使用了canvas元素。使用WebGL进行图形渲染至少要经历以下步骤:
1. 创建一个画布元素。
2. 获取画布的上下文。
3. 初始化视口。
4. 创建一个或多个包含渲染数据的数组(通常是顶点数组)。
5. 创建一个或多个矩阵,将顶点数组变换到屏幕空间。
6. 创建一个或多个着色器来实现绘制算法。
7. 使用参数初始化着色器。
8. 绘制。
一、画布元素与绘制上下文
使用WebGL只需要创建一个canvas元素,通过与该元素关联的DOM对象获取WebGL上下文:
function initWebGL(canvas){
var gl;
try{
gl=canvas.getContex("expremental-webgl");
}
catch(e){
var msg = "Error creating webgl context: "+ e.toString();
alert(msg);
throw Error(msg);
}
return gl;
}
二、视口
获取了WebGL上下文就需要指定在哪里绘制图形,这就称为视口(viewport),调用gl的viewport()函数进行视口的初始化:
function initViewport(gl,canvas){
gl.viewport(0,0,canvas.width,canvas.height);
}
其中的四个参数为画布左上角和右下角的坐标。
三、Buffer、ArrayBuffer和类型化数组
WebGL的绘制是由图元组成的,图元的种类包括三角形(三角形数组)、三角形带(triangle strip)、点和线。图元使用的数据数组被称为Buffer,定义了顶点的位置。以下代码定义了一个1×1的正方形顶点数组。
function createSquare(gl){
var vertexBuffer;
vertexBuffer = gl.createBuffer();//创建顶点缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);//将创建的缓冲区绑定到gl数组
var vertex = [//定义四个顶点的位置
.5, .5, 0.0,
-.5,.5, 0.0,
.5, -.5, 0.0,
-.5, -.5, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertex),gl.STATIC_DRAW);//绑定位置数据到gl数组
var square = {buffer:vertexBuffer,vertSize:3,nVerts:4,primtype:gl.TRIANGLE_TRIP};
return square;
}
其中vertSize和nVerts分别定义了一个顶点中元素的个数和顶点的个数,此例中表明有4个顶点,每个顶点3个元素。Float32Array是为了WebGL专门引入浏览器的数据类型,称为类型化数组,是ArrayBuffer的一种,用于js中存储二进制数据。
四、矩阵
在绘制之前需要建立两个矩阵——模型视图矩阵(modelview matrix)和投影矩阵(projection martrix)。模型视图矩阵用于定义3D坐标系中正方形相对于相机的位置,投影矩阵用于在着色器中将相机空间中的3D坐标准环卫绘制视口的2D坐标。在本例中对模型的变换是沿-z轴平移3.333个单位,即远离相机3.333个单位,投影矩阵定义了一个45°的相机视野。
function initMatrices(){
modelViewMatrix = new Float32Array(
[1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,-3.333,1
]);
projectionMatrix = new Float32Array([
2.41421,0,0,0,
0,2.41421,0,0,
0,0,-1.002002,-1,
0,0,-0.2002002,0
]);
}
在一般开发中,很少有人会自己动手计算投影矩阵,因为一半计算非常复杂,大多都时通过框架来完成。比较有名的框架为glMatrix,专门在js中进行矩阵的运算。
五、着色器
着色器是一小段由类C的高级语言编写的,一般为GLSL ES语言,每个需要绘制的物体都要提供着色器,一个着色器可以应用于多个物体。在实际应用中通常只提供一个着色器,通过不同的参数多次复用。
着色器包括两个部分——顶点着色器(vertex shader)和片元着色器(fragment shader),后者也被称为像素着色器。顶点着色器负责将物体的坐标转换到2D空间去,片元着色器负责将颜色输出到每个转换后的顶点像素。颜色的输出可以为纯色、纹理、光源或者材质。
var vertexShaderSource =
" attribute vec3 vertexPos;\n"+
" uniform mat4 modelViewMatrix;\n"+
" uniform mat4 projectionMatrix;\n"+
" void main(void) {\n"+
" //返回变换并投影后的顶点数据\n"+
" gl_Position = projectionMatrix*modelViewMatrix*\n"+
" vec4(vertexPos,1.0);\n"+
" }\n";
var fragmentShaderSource =
" void main(void) {\n"+
" //返回像素颜色,这里为白色\n"+
" gl_FragColor = vec4(1.0,1.0,1.0,1.0);\n"+
" }\n";
六、绘制图元
我们定义了一个函数draw()来完成图元的绘制,在该函数中我们将接管上面定义的WebGL上下文以及正方形对象。首先函数会清理一下画布并用黑色填充作为背景色。将顶点数组绑定到上下文中,使用着色器并把顶点数组和矩阵作为输入传给着色器。最后调用WebGL的drawArrays()绘制正方形。
function draw(gl,obj){
//用黑色清空背景
gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
//设置顶点数组
gl.bindBuffer(gl.ARRAY_BUFFER,obj.buffer);
//设置着色器
gl.useProgram(shaderProgram);
//设置着色器参数:顶点坐标、投影矩阵和模型视图矩阵
gl.vertexAttribPointer(shaderVertexPositionAttribute,obj.vertSize,gl.FLOAT,false,0,0);
gl.uniformMatrix4fv(shaderProjectionMatrixUniform,false,projectionMatrix);
gl.uniformMatrix4fv(shaderModelViewMatrixUniform,false,modelViewMatrix);
//绘制物体
gl.drawArrays(obj.primtype,0,obj.nVerts);
}
最终的绘制结果如图所示: