在WebGL环境中添加2D内容

本系列文章翻译自:https://developer.mozilla.org/en/WebGL

本文地址:https://developer.mozilla.org/en/WebGL/Adding_2D_content_to_a_WebGL_context

翻译说明:水平有限并且不会逐字逐句翻译,只保证符合原文意思。

一旦你成功创建WebGL环境,就可以开始在里面渲染内容了。最简单的莫过于画一个没有任何纹理的2D图形,所以我们将从这里开始,编写代码来画一个方形。

设置场景的光源

在开始之前你需要理解这一点:即使我们画的是二维图形,也是在三维空间绘制的。所以我们除了绘制形状以外仍然需要建立着色器(shader)来照亮这个简单的场景。

初始化着色器

着色器是通过OpenGL ES着色语言来定义的。为了使我们的内容更方便的维护和更新,我们可以写一段代码来加载着色器,这样在整个HTML文档中都可以使用。下面就是initShaders()函数的内容,它来负责加载着色器。

function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");

// 创建着色器程序

shaderProgram
= gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

// 如果失败则进行提示

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert(
"Unable to initialize the shader program.");
}

gl.useProgram(shaderProgram);

vertexPositionAttribute
= gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute);
}

这里,代码首先加载了两个着色器程序;第一个是片段着色器,它是从一个id为“shader-fs”的script元素中加载的。第二个是顶点着色器,从id为“shader-vs”的script元素加载。下一节我们将看到getShader()函数的内容,它用来从DOM中获取着色器程序。

接着调用WebGL对象的createProgram()方法来创建着色器程序,并将之前创建的两个着色器附加到WebGL对象上,再连接到着色器程序中。之后,我们检测gl对象的LINK_STATUS参数确保程序连接成功。如果连接成功,我们就激活了这个新的着色程序。

从DOM中加载着色器

getShader()函数通过id名从DOM中获取着色器并返回给调用者,如果无法找到着色器或者编译失败则返回null。

function getShader(gl, id) {
var shaderScript = document.getElementById(id);

if (!shaderScript) {
return null;
}

var theSource = "";
var currentChild = shaderScript.firstChild;

while(currentChild) {
if (currentChild.nodeType == 3) {
theSource
+= currentChild.textContent;
}

currentChild
= currentChild.nextSibling;
}

一旦找到元素,其中的内容就会被读到变量theSource中。

  var shader;

if (shaderScript.type == "x-shader/x-fragment") {
shader
= gl.createShader(gl.FRAGMENT_SHADER);
}
else if (shaderScript.type == "x-shader/x-vertex") {
shader
= gl.createShader(gl.VERTEX_SHADER);
}
else {
return null; // 未知着色器类型
}

当着色器的代码读取完毕,我们查看着色器对象的MIME类型,由此决定是顶点着色器(MIME类型为“x-shader/x-vertex”)还是片段着色器(MIME类型为“x-shader/x-fragment”),接着创建与此对应的着色器。

  gl.shaderSource(shader, theSource);

// 编译着色器

gl.compileShader(shader);

// 检查编译是否成功

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(
"An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
return null;
}

return shader;
}

最后,theSource变量的内容被传到着色器中进行编译。如果失败则提示用户并返回null,否则将着色器返回给调用者。

着色器

下面我们需要添加着色器程序本身到HTML中。着色器通过着色器语言的语法进行描述,其工作原理的细节已经超出本文范围。

片段着色器

在GL术语中,多边形的每一个像素被称作一个片段。片段着色器的工作就是为每一个像素添加颜色。这里我们简单地设置颜色为白色。

gl_FragColor是一个内置的GL变量。通过它可以设置像素的颜色。

<script id="shader-fs" type="x-shader/x-fragment">
void main(void) {
gl_FragColor
= vec4(1.0, 1.0, 1.0, 1.0);
}
</script>

顶点着色器

顶点着色器定义每个顶点的位置和形状。

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;

void main(void) {
gl_Position
= uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
}
</script>

创建对象

在开始渲染方形之前,我们需要创建一个容纳顶点的缓冲区,这将通过调用函数initBuffers()来完成;随着我们逐渐深入WebGL,这个过程将被扩充用以创建更多的3D对象。

var horizAspect = 480.0/640.0;

function initBuffers() {
squareVerticesBuffer
= gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);

var vertices = [
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
];

gl.bufferData(gl.ARRAY_BUFFER,
new Float32Array(vertices), gl.STATIC_DRAW);
}

函数内容非常简单。开始它调用gl的createBuffer()方法获得一个缓冲区,我们会在其中放入顶点。该缓冲区通过bindBuffer()方法会被绑定到环境中。

下面,我们创建一个Javascript数组用来保存矩形的每个顶点。这些信息会被转换为WebGL浮点数并传递给gl的bufferData()方法,用来建立对象的顶点。

绘制场景

着色器建立好之后,我们就可以真正渲染场景了。因为本例中没有任何动画,函数drawScene()函数非常简单。它使用了几个工具库,稍后会介绍。

function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT
| gl.DEPTH_BUFFER_BIT);

perspectiveMatrix
= makePerspective(45, 640.0/480.0, 0.1, 100.0);

loadIdentity();
mvTranslate([
-0.0, 0.0, -6.0]);

gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
gl.vertexAttribPointer(vertexPositionAttribute,
3, gl.FLOAT, false, 0, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLE_STRIP,
0, 4);
}

第一步清除环境只保留背景色;下面建立摄像机视角。这里设置为45度,宽高比为640/480。另外我们规定距离摄像机0.1到100个单位之间的物体才会被渲染。

接下来加载位置信息并将其放置在距离摄像机6个单位的位置。之后再将方形的顶点缓冲区绑定到环境中并进行配置,最后调用drawArrays()方法来绘制对象。

矩阵工具库

矩阵操作很复杂。没有人愿意完全编写所有相关的代码。好在有个叫做Sylvester的库,可以在JavaScript中处理向量和矩阵操作。

这个范例中的glUtils.js文件也在其他的WebGL范例中引用。没人知道它从何而来,但是它添加了许多建立特殊矩阵的方法,确实比Sylvester用起来更简单,另外它还可以通过HTML格式进行输出。

此外,这个范例定义了几个有用的方法。这些方法的细节已经超出范例的范围,网上有很多关于矩阵的参考资料,可以查看后面的参考资料部分。

function loadIdentity() {
mvMatrix
= Matrix.I(4);
}

function multMatrix(m) {
mvMatrix
= mvMatrix.x(m);
}

function mvTranslate(v) {
multMatrix(Matrix.Translation($V([v[
0], v[1], v[2]])).ensure4x4());
}

function setMatrixUniforms() {
var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
gl.uniformMatrix4fv(pUniform,
false, new Float32Array(perspectiveMatrix.flatten()));

var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
gl.uniformMatrix4fv(mvUniform,
false, new Float32Array(mvMatrix.flatten()));
}

参考资料

Matrices Wolfram MathWorld

Matrix on Wikipedia

posted on 2011-08-22 17:16  jz1108  阅读(2626)  评论(3编辑  收藏  举报