webGL 学习手记 | webGL 教程 (一)

闲扯淡

若干年前(那时候还不知道雾霾是什么),一个偶然的机会了解到了WebGL。当时出于好奇便找了些资料,想深入研究,但是由于各种原因放弃了。若干年后,来到了充满“黑暗雾霾故事“的帝都追随梦想,由于工作的原因,再次对webGL充满了浓厚额兴趣,为了不让自己的意志被时间消磨,我决定把webGL的过程写成文章,一是用来鞭励自己,二是坚持互联网的开源分享精神,给后面赶路的童鞋们提供些便利(其实最重要的是可以给博客吸引人气,有木有!![贱萌的笑])。

一些介绍

因为工作比较繁忙,绝大部分的东西是在业余时间进行研究的,而且OPEN GL方面确实需要些数学基础,所以进度,以及能研究的到多深入似乎取决于我的智商,所以还请各位多多理解。

另外:我将学习过程中用到的一些资料放在了 GithubYes-webGL https://github.com/zmofei/yes-webgl 项目上,希望能遇到志同的人一起来完善这个事情。

好了闲话少说,我们开始步入正题。

DEMO

前几篇文章会先从如何用webGL实现2D图案开始来介绍,我会说从最基础的部分说起(可能会比较枯燥,请自备男/女朋友、瓜子、花生等解闷)。

此次DEMO,我们在画布中通过WebGL绘制一个红色矩形:

YES!webGL!我们开始吧

开始,还是大致介绍一下webGL实现的过程。

实现webGL你需要理解下面的一些概念,canvas,vertex shader,fragment shader,buffer

  • canvas : 画布,如果用过canvas的同学应该很熟悉它了,webGL也是通过canvas画布展现出来的。
  • vartex shader : 顶点着色器,用来储存图像的位置相关信息,比如坐标、大小等。
  • fragment shader : 片远着色器,用来描述对象的颜色文理等信息。
  • buffer : 缓冲区,通常情况下如果绘制多个点,或者繁杂的纹理的时候会特别使用到buffer object,其他情况下,图形会在该区域进行缓冲,缓冲完成之后显示在屏幕上。

那么,这些名词是如何协同工作的呢?先让我们看一张图:

webGL渲染实例图

上图描述了webGL处理图形的简单过程:

  1. 首先通过canvas获取webgl的上下文。
  2. 通过vartex shader(顶点着色器)和 fragment shader(片元着色器)指定图形的形状和样式。
  3. 将这些图形颜色等数据放入相应的缓冲区。
  4. 绘制在显示器上。

单纯从字面意思来说可能不是在么好理解,下面结合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的相关知识。

Write a response...
Mofei Zhu
publish
Mofei
2014-11-27 10:48
@ 我自己用node写的一个博客,基于我自己开发的一个msite(http://msite.zhuwenlong.com/)
1
 Replay
@Mofei  
Replay
匿名
2014-11-27 10:43
这个是什么博客呢?
0
 Replay
@  
Replay