47

WebGL入门与进阶1

 5 years ago
source link: https://jdc.jd.com/archives/212363?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

改革开发40年以来,世界日新月异,无论从生活到精神上都有了颠覆性的变化,曾经教授还是教书的,砖家还叫专家,太阳还不叫日,菊花还是一种花,老王还没那么多,Web还只需要做IE,XHR还没出生的时候,怎么能想到现在浏览器会提供如此丰富多彩的多媒体生活,无论是音频、视频、以及各种漂亮的页面都在让用户更好的拥抱着互联网,当二维页面无法满足用户之后,会出现什么样的内容来继续推进Web进展呢,没错,就是3D,浏览器中看到的内容从平面变成3D的时候,oH,My God,提起来都让人兴奋。

网站的未来是这样子的:

MNvaYvq.gif

的确,3D技术会让平淡的网页变的更酷,更让人眼花撩眼,这本后隐藏着什么呢?来来,咱们做下来泡一壶茶,边喝边聊,这个神奇的家伙叫做 WebGL

谈起WebGL可能有一些人比较陌生,实际上WebGL是一种3D绘图标准,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏等等。

此链接可以查看你的游览器是否支持WebGL以及支持的版本[1]。

看WebGL的背景实际上是JavaScript操作一些OpenGL接口,也就意味着,可能会编写一部分GLSL ES 2.0的代码,没错,你猜对了,WebGL只是绑定了一层,内部的一些核心内容,如着色器,材质,灯光等都是需要借助GLSL ES语法来操作的.

基于WebGL周边也衍生了众多的第三方库,如开发应用类的Three.js,开发游戏类的Egert.js等,都大大的降低了学习WebGL的成本,但是本着有问题解决问题,没问题制造问题在解决问题的程序猿态度,还是觉得应该稍微了解一下WebGL一些基本的概念,以便能更好的去理解不同框架带来的便捷以及优势。

接下来先简单介绍一下使用到的知识要点。

创建webGL对象

不同浏览器生命WebGL对象方式有所区别,虽然大部分浏览器都支持experimental-webgl,而且以后会变成webgl,所以创建时做一下兼容处理

var canvas = document.getElementById("glcanvas");
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");

着色器

WebGL依赖一种新的称为着色器(shader)的绘图机制。着色器提供了灵活且强大的绘制二维或三维图形的方法,所有WebGL必须使用它。着色器不仅强大,而且更复杂,仅仅通过一条简单的绘图指令是不能操作它的。

WebGL需要两种着色器

  • 顶点着色器(Vertex shader):顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。 顶点(Vertex) 是指二维或三维空间的一个点,比如二维或三维空间线与线之间的交叉点或者端点。
  • 片元着色器(Fragment shader):进行逐片元处理过程(如光照等)的程序。 片元(fragment) 是一个WebGL的术语,你可以将其理解成像素。

着色器语言使用的是GLSL ES语言,所以在javascript需要将之存放在字符串中,等待调用编译

创建顶点着色器:

var VSHADER_SOURCE =
 'void main() {\n' +
   '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' +
   '  gl_PointSize = 10.0;\n' +
 '}\n';

创建片元着色器:

var FSHADER_SOURCE =
   'void main() {\n' +
   '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';

浏览器的整个过程如下:

VBRJj2m.jpg!web

着色器中包含几个内置变量:gl_Position、gl_PointSize、gl_FragColor。

着色器语言中涉及到vec4的数据类型,此数据类型是一个思维浮点数组,所以其值不可以是整形如(1,1,1,1), 正确应为:(1.0,1.0,1.0,1.0)

  • gl_Position : 为一种vec4类型的变量,且必须被赋值。四维坐标矢量,我们称之为 齐次坐标 ,即(x,y,z,w)等价于三维左边(x/w,y/w,z/w),w相当于深度,没有特殊要求设置为1.0即可。
  • gl_PointSize :表示顶点的尺寸,也是浮点数,为非必填项,如果不填则默认显示为1.0。
  • gl_FragColor :该变量为片元着色器唯一的内置变量,表示其颜色,也是一个vec4类型变量,分别代表(R,G,B,A),不过颜色范围是从 0.0-1.0 对应Javascript中的 #00-#FF

有了着色器我们就可以着手去绘制图像了,既然绘制3D图形,必然会有对应的三维坐标系,WebGL采用右手坐标系,如图所示:

NvQr6nZ.jpg!web

使用着色器

让我们来看看如何把着色器代码编译并且使用起来

着色器代码需要载入到一个程序中,webgl使用此程序才能调用着色器。

var program = gl.createProgram();
// 创建顶点着色器
var vShader = gl.createShader(gl.VERTEX_SHADER);
// 创建片元着色器
var fShader = gl.createShader(gl.FRAGMENT_SHADER);
// shader容器与着色器绑定
gl.shaderSource(vShader, VSHADER_SOURCE);
gl.shaderSource(fShader, FSHADER_SOURCE);
// 将GLSE语言编译成浏览器可用代码
gl.compileShader(vShader);
gl.compileShader(fShader);
// 将着色器添加到程序上
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
// 链接程序,在链接操作执行以后,可以任意修改shader的源代码,
对shader重新编译不会影响整个程序,除非重新链接程序
gl.linkProgram(program);
// 加载并使用链接好的程序
gl.useProgram(program);

让我们尝试绘制一个点

gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0 ,1);

我们来看一看最终结果,果然出来了一个点。

nYB7fyN.jpg!web

Why? 咋这么模糊?没错不是你的眼镜度数又高了,的确是模糊的。

让我们来说说WebGL的坐标系。

因为WebGL的坐标系与实际页面中的坐标系是不同的,如下图,普通canvas坐标系与正常的浏览器像素值相同,但WebGL中的坐标系是以整个WebGL中心点为(0.0,0.0),而且坐标的精确度为小数点后一位。坐标系对比如下图所示:

JJva6rY.jpg!web

图上的实例是在使用源生WebGL时,并且未对视角有设置的情况下的默认值。默认视角的位置为(0.0, 0.0, 0.0),并且lookAt(0.0, 0.0, -1.0),也就是默认的视角是看向z轴负坐标的,如果点在z的正位置上,则是无法看到点的。

绘制之后,发现这个依旧会绘制出超级模糊的图像,那是因为整个WebGL的尺寸是与canvas宽度与高度相关连的,并且canvas的宽度与高度如果用css来设置的话,会被默认成100×100,也就意味着,你绘制出来的图形是把100×100的图形拉伸到当前canvas的尺寸中。所以正确的设置canvas的方式应该如下:

//错误
<canvas id="glcanvas" style="width: 700px; height: 500px;">
//正确的方式
<canvas id="glcanvas" width="700" height="500">

一个真实尺寸的清晰的点就出现了。

7vu67fQ.jpg!web

但我们辛苦绘制出来的点居然是正方形的,但WebGL是未提供绘制圆点的方法。

我们首先来了解一下WebGL的渲染机制,顶点着色器的信息在传递给OpenGL底层绘制的时候,会先进行光栅化,也就是把点转化成对应的像素。然后在片元着色器会逐个点进行渲染,最终就达到了视觉看到的效果。

bQjeau6.gif

点也是一样,会将点转变成多个像素点,所以要变成圆点,需要如下的方式:

UVv2IjZ.jpg!web

我们需要在片元着色器中来处理,将非原型区域的像素点,不用片元着色器来绘制,着色器需要判断距离圆点的位置超过0.5的话,就忽略此片元点,最终就会出现一个圆点的效果。

var FSHADER_SOURCE = `
#ifdef GL_ES
    precision mediump float;
#endif
void main() {
 float d = distance(gl_PointCoord, vec2(0.5,0.5));
   if(d < 0.5){
     gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
   }else{ discard;}
}`;

如下图,我们绘制了一个圆点:

AV36vaf.png!web

本章我们已经了解了WebGL的一些背景,以及如何使用一些基本功能,那么如何把着色器动态化并且同时高性能的绘制大量点呢?下一章我们会解开WebGL缓存区的面纱。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK