使用 WebGL 创建 2D 内容
Posted H5技术社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 WebGL 创建 2D 内容相关的知识,希望对你有一定的参考价值。
一旦创建WEBGL上下文成功,你就可以在这个上下文里渲染画图了。而对我们而言最简单的事,莫过于绘制一个没有纹理的2D图形了。那就让我们从画出一个正方形开始吧。
渲染场景
在开始前,我们首先需要明确最重要的一点,就是虽然我们的例子只是画一个二维物体,但我们仍然是在把它画在一个三维空间里。所以,我们依然需要创建着色器,通过它来渲染我们的简单场景并画出我们的物体。往下,我们将展示正方形是怎样一步步被画出来的。
初始化着色器
WebGL着色器使用 OpenGL ES Shading Language. 为了更方便地处理和更新内容,事实上我们可以将着色器代码写在html文档上,而不是把所有代码都写在 javascript里。下面我们看一下 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() 函数从DOM元素里抓取着色器程序,并返回一个编译好的着色器程序,但是,如果它加载失败或编译失败的话,将返回 null 。
function getShader(gl, id) {
var shaderScript, theSource, currentChild, shader;
shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
theSource = "";
currentChild = shaderScript.firstChild;
while(currentChild) {
if (currentChild.nodeType == currentChild.TEXT_NODE) {
theSource += currentChild.textContent;
}
currentChild = currentChild.nextSibling;
}
复制代码
一旦找到指定ID的元素,其文本内容将被读取保存到变量 theSource 。
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 {
// Unknown shader type
return null;
}
复制代码
一旦读取到着色器源码,我们就根据着色器对象的 MIME 属性来判断它是顶点着色器(MIME type "x-shader/x-vertex"),还是片段着色(MIME type "x-shader-x-fragment"),从而创建相应的着色器。
gl.shaderSource(shader, theSource);
// Compile the shader program
gl.compileShader(shader);
// See if it compiled successfully
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
复制代码
最后,源码将传到着色器上并编译。如果在编译途中发生错误,我们会显示一个警告并返回 null,否则,就返回一个新的编译好的着色器。
着色器
接着,我们需要将着色器程序代码加入到 HTML 文档。但是,着色器的语法和具体怎样工作已经超出本篇文章讨论的范围。
片段着色器
在 WebGL 多边形中的每一个像素都叫一个片段。这个片段着色器的工作就是建立每个像素的色彩。在这个案例中,我们会简单地给每个像素填上白色。
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 的高级概念时,这段代码会将有更多参数,变得更加复杂,并且用来创建更多的三维物体。
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() 方法来画出对象。
如果你的浏览器支持WebGL的话,可以点击这里看看DEMO。
矩阵通用计算
矩阵计算是一个很复杂的运算。 没人会想去自己写完所有代码来处理这些运算。幸运的是,这里有 Sylvester, 一个使用方便的 JavaScript处理向量和矩阵运算的库。
在这个例子中使用的 glUtils.js 文件同时也被使用到很多的其它WebGL例子中去了。似乎没有人完全清楚它的来源,但是它简化了对Sylvester 的使用,甚至添加了一些生成特殊矩阵和输出显示它们的方法函数。
另外,这个例子给出了几个有用的代码,它结合这些库来解决特定的任务。准确来说它们已经超出了这个例子讨论的范围,但是这里有一些非常好的有关于矩阵运算的在线文档。可以参考看See also 栏目找到相关资料。
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()));
}
复制代码
以上是关于使用 WebGL 创建 2D 内容的主要内容,如果未能解决你的问题,请参考以下文章
Three.js/WebGL 和 2D Canvas -- 将 getImageData() 数组传递给 Three.DataTexture()