webGL 学习手记 | webGL 教程 (一)
闲扯淡
若干年前(那时候还不知道雾霾是什么),一个偶然的机会了解到了WebGL。当时出于好奇便找了些资料,想深入研究,但是由于各种原因放弃了。若干年后,来到了充满“黑暗雾霾故事“的帝都追随梦想,由于工作的原因,再次对webGL充满了浓厚额兴趣,为了不让自己的意志被时间消磨,我决定把webGL的过程写成文章,一是用来鞭励自己,二是坚持互联网的开源分享精神,给后面赶路的童鞋们提供些便利(其实最重要的是可以给博客吸引人气,有木有!![贱萌的笑])。
一些介绍
因为工作比较繁忙,绝大部分的东西是在业余时间进行研究的,而且OPEN GL方面确实需要些数学基础,所以进度,以及能研究的到多深入似乎取决于我的智商,所以还请各位多多理解。
另外:我将学习过程中用到的一些资料放在了 Github
的 Yes-webGL
https://github.com/zmofei/yes-webgl 项目上,希望能遇到志同的人一起来完善这个事情。
好了闲话少说,我们开始步入正题。
DEMO
前几篇文章会先从如何用webGL实现2D图案开始来介绍,我会说从最基础的部分说起(可能会比较枯燥,请自备男/女朋友、瓜子、花生等解闷)。
此次DEMO,我们在画布中通过WebGL绘制一个红色矩形:
- DEMO预览
- http://zmofei.github.io/yes-webgl/demo/drawpoint.html
- DEMO源码
- https://github.com/zmofei/yes-webgl/blob/master/demo/drawpoint.html
YES!webGL!我们开始吧
开始,还是大致介绍一下webGL实现的过程。
实现webGL你需要理解下面的一些概念,canvas
,vertex shader
,fragment shader
,buffer
- canvas : 画布,如果用过canvas的同学应该很熟悉它了,webGL也是通过canvas画布展现出来的。
- vartex shader : 顶点着色器,用来储存图像的位置相关信息,比如坐标、大小等。
- fragment shader : 片远着色器,用来描述对象的颜色文理等信息。
- buffer : 缓冲区,通常情况下如果绘制多个点,或者繁杂的纹理的时候会特别使用到
buffer object
,其他情况下,图形会在该区域进行缓冲,缓冲完成之后显示在屏幕上。
那么,这些名词是如何协同工作的呢?先让我们看一张图:
上图描述了webGL处理图形的简单过程:
- 首先通过canvas获取webgl的上下文。
- 通过vartex shader(顶点着色器)和 fragment shader(片元着色器)指定图形的形状和样式。
- 将这些图形颜色等数据放入相应的缓冲区。
- 绘制在显示器上。
单纯从字面意思来说可能不是在么好理解,下面结合DEMO的代码进行详细的说明。
一、获取webGL执行环境
HTML
<canvas id="webgl"></canvas>
JavaScript
var canvas = document.getElementById('webgl');
var webgl = canvas.getContext('webgl');
这一步很简单,和canvas一样,我们在进行webGL渲染之前,需要先获得webgl的执行环境(即上下文),首先我们通过getElementById
获取到了我们用来渲染webGL的DOM元素,然后通过getContext('webgl')
来获取webGL上下文。
但是需要留意的是,在DEMO中我们并为对浏览器的兼容性进行处理,实际上getContext
的参数可能为下面四个中的一个["webgl","experimental-webgl","webkit-3d","moz-webgl"]
,第一个就不用多解释,第二个出现在webgl还是个实验性功能的时候,后面两个从前缀中不难判断出他们的用处。
所以,通常情况下在正式的项目中,我们需要这样一个方法来获取webGL的上下文(为了DEMO的足够简单,在DEMO中并没有体现出来,后续会放一个google写的用来初始化webGL的一个“类库”)。
/**
* Creates a webgl context.
* @param {!Canvas} canvas The canvas tag to get context
* from. If one is not passed in one will be created.
* @return {!WebGLContext} The created context.
*/
var webglContext = function(canvas) {
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
var context = null;
for (var ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii]);
} catch(e) {}
if (context) {
break;
}
}
return context;
}
拿到webGL上下文之后我们可以打印出来其具体的值然后简单的看一下它的__proto__
:
"__proto__":{
ACTIVE_ATTRIBUTES: 35721,
ACTIVE_TEXTURE: 34016,
ACTIVE_UNIFORMS: 35718,
ALIASED_LINE_WIDTH_RANGE: 33902,
ALIASED_POINT_SIZE_RANGE: 33901,
ALPHA: 6406ALPHA_BITS: 3413,
//...
activeTexture: function activeTexture() { [native code] },
attachShader: function attachShader() { [native code] },
bindAttribLocation: function bindAttribLocation() { [native code] },
bindBuffer: function bindBuffer() { [native code] }
//...
}
在他的原型链上我们看到了一些类似ACTIVE_ATTRIBUTES
之类的定值,这些值是这些属性的标识,后续的很多操作是通过这些标识来绑定到webGL对象上的,比如:
//创建vertex shader
webgl.createShader(webgl.VERTEX_SHADER);
//清除颜色缓冲区
webgl.clear(webgl.COLOR_BUFFER_BIT);
这些方法中的参数就是webGL原型链中的一些定值(比如VERTEX_SHADER:35633
),当然了你也可以用35633代替webgl.VERTEX_SHADER,其仍然能正正常工作,至于这些值是固定的么?我尝试换了几个浏览器,通过console.log打印出来发现,他们都是固定的数值,我的理解是,这些数字是固定的,可以标识webGL对象的某些属性。就像我们的身份证号码一样,一个号码标识了一个人。
结语
OK,获取到了webGL之后,我们就要去渲染图形了,下一次我会和大家说一些关于vertex shader 和 fragment shader的相关知识。