WebGL ThreeJS学习总结一、二

© Young 2016-10-18 07:57
Welcome to My GitHub

概述

在写这篇总结之前,这已经是我第二次尝试学习WebGL了,第一次是在两年前,那时候正在从事Java开发相关工作,觉得没什么意思,然后平时工作中有接触到一些前端相关的东西,刚好那时HTML5很火,就稍微了解了一下Canvas,然后自然而然的知道了WebGL,刚开始学的时候感觉很吃力,然后就买了本书《WebGL入门指南》,虽然跟着书上的例子也能照葫芦画瓢弄个正方体出来,但是总感觉实现了某个例子,依然啥也不会,没多久就放弃了。

现在已经从事前端开发工作接近两年,在前不久看到一篇博客后再次燃起了学习WebGL的热情,然后采购了一本书《WebGL编程指南》,花了一周时间看了100多页之后,差点再次放弃,思考了一下午决定改进下学习方法。

结合这两次的从准备入门到放弃的经验来看,单独学习ThreeJS容易给人造成不知其然的感觉,而单独学习WebGL又太枯燥,所以我决定把二者结合起来,先根据《WebGL编程指南》学习WebGL感觉枯燥之后就参照ThreeJS官方文档以及官方源代码中的例子学习ThreeJS并找些相对容易实现的例子实现,在这过程中写一些学习总结。

总结一、二主要是了解一些WebGL的基本概念,然后通过示例程序学习WebGL程序的基本结构、GLSL语言以及编写一些简单的WebGL程序。

概念

一、基本概念

1、WebGL起源

WebGL派生于OpenGL,目前在浏览器中被广泛支持。

2、WebGL程序结构

WebGL程序和普通的JavaScript程序不一样,WebGL程序除了JavaScript部分之外,还包含两个使用GLSL编程语言的着色器程序,分别是顶点着色器和片元着色器;

3、WebGL坐标系

在WebGL中一般使用的坐标系是当你面朝计算机屏幕时,X轴是水平的(正方向为右),Y轴是垂直的(正方向为上),Z轴垂直于屏幕(正方向为外),这个坐标系也被称为右手坐标系

之所被称为右手坐标系是因为它是通过如图所示的右手手势确定的,即当你伸出右手摆出如图所示手势时,拇指指向X轴的正方向食指指向Y轴的正方向中指指向Z轴的正方向,这种确定坐标系方式也被称为右手定则

另外坐标系的取值范围为[-1.0,1.0]

4、WebGL颜色分量取值范围

WebGL遵循传统OpenGL颜色分量的取值范围[0.0,1.0],这和2D Canvas、CSS、SVG等常用的图形技术颜色分量的取值范围[0,255]并不一样。

5、三角形网格

当你在纸上手绘正方体时,其实就是先确定八个顶点,然后再这八个顶点之间相互连线,每四个顶点组成一个正方形面,最终由六个正方形面组成正方体;

在计算机中构建3D物体也是如此,只不过采用的是连接三个顶点组成三角形的方式而已,这个三角形在计算机图形学中被称为三角形网格

一般来说网格面数越多,物体越精细,但同时会消耗更多的存储空间以及计算机性能。

之所以采用三角形网格则是因为三角形网格具有很多优点,比如三角形是最简单的多边形、三角形经过多种变换之后依然是三角形等等。

6、 材质和纹理

三角形网格只能描述物体的轮廓,但现实生活中的物体,人眼除了能看到其轮廓之外,还能看到其材质,比如一般情况下人能通过眼睛分辨出哪些金属哪些不是金属,因为金属和非金属对光的反射效果不一样;可以理解为材质主要是用来描述物体表面动态属性的对象,比如处理光照等。

7、着色器

在计算机中创建一个3D物体,需要轮廓材质纹理三个要素;

那么可以简单的理解为顶点着色器是用来处理物体轮廓的程序,片元着色器是用来处理物体材质和纹理的程序

9、片元

片元可以简单理解为像素点,也就是下图中的小方块;

在构建3D物体时通过顶点组成三角形网格,但这些三角形网格都是矢量图形,最终在屏幕上显示时还是需要转化为像素图形,这种转化过程被称为光栅化,是计算机图形学的关键技术之一。

10、图形装配和光栅化

  • 执行顶点着色器,传入缓冲区对象中的第一个顶点坐标,一旦赋值成功,该数据就进入了图形装配区域,并暂时存储在那里;

  • 重复执行顶点着色器直到所有顶点数据赋值完成;

  • 开始装配图形(按照一定的规则把所有顶点连接起来);

  • 将图形转化为片元,这个过程被称为光栅化,光栅化是三维图形学的关键技术之一,它负责将矢量的几何图形转变为栅格化的片元

补充解释下光栅化,其实就是把矢量图形转化成像素点的过程,因为最常用的一种绘制3D图形的方法就是使用网格(Mesh),也就是点线面属于矢量图形,而当前屏幕是像素渲染的,那么从矢量图形转化成用户所看到的像素图像必然需要光栅化这一步骤。

  • 光栅化结束之后,程序就开始逐片元调用片元着色器,每调用一次就处理一个片元,片元着色器会计算出该片元的颜色,并写入颜色缓冲区;

  • 当最后一个片元处理完成,浏览器就会显示最终结果。

另外这个过程如果我们使用当前流行的ThreeJS框架可以理解如下:

其中黄色区域是ThreeJS框架中使用JS实现的,绿色部分是THreeJS框架中使用GLSL ES实现的。

11、缓冲区对象

对于那些由多个顶点组成的图形,需要一次性地将图形的顶点全部传入顶点着色器,然后才能把图形绘制出来,WebGL提供了一种很方便的机制,即缓冲区对象(WebGL系统中的一块内存区域),它可以一次性地向着色器传入多个顶点数据。

使用缓冲区对象时需要遵循以下五个步骤:

创建缓冲区对象(gl.createBuffer());
绑定缓冲区对象(gl.bindBuffer());
将数据写入缓冲区对象(gl.bufferData());
将缓冲区对象分配给着色器变量(gl.vertexAttriPointer());
启动着色器变量(gl.enableVertexAttriArray())。

使用多个缓冲区对象向着色器传递多种数据,比较适合数据量不大的情况,当数据量很大时这种方式很难维护,所以WebGL允许我们把不同种类的数据打包到同一个缓冲区对象中,并通过某种机制分别访问缓冲区对象中不同种类的数据。

12、纹理坐标

纹理坐标是纹理图像上的坐标,通过纹理坐标可以在纹理图像上获取纹素颜色,WebGL系统中的纹理坐标系统是二维的,为了将纹理坐标和广泛使用的x坐标和y坐标区分开来,使用s和t命名,称之为st坐标系统。

13、纹理映射

纹理映射的一般步骤:

  • 准备好映射到几何图形上的纹理图像;
  • 为几何图形配置纹理映射方式;
  • 加载纹理图像,对其进行一些配置,以在WebGL中使用它;
  • 在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋予片元。

在配置纹理映射的时候需要注意一点,图片坐标系统和WebGL纹理坐标系统的Y轴方向是相反的,这时候你需要对纹理图像进行Y轴反转;

  • gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);// Flip the image’s y-axis

14、纹理单元

WebGL通过一种纹理单元的机制来同时使用多个纹理,每个纹理单元有一个单元编号来管理一张纹理图像,系统支持的纹理单元个数取决于硬件和浏览器的WebGL实现,默认情况下至少支持8个纹理单元。

使用纹理单元的一般步骤:

  • 激活纹理单元(gl.activeTexture());
  • 绑定纹理对象(gl.bindTexture());
  • 配置纹理对象的参数(gl.texParameteri());
  • 将纹理图像分配给纹理对象(gl.texImage2D());
  • 将纹理单元编号传递给取样器。

15、FrameBuffer

有点类似于Canvas中的离屏渲染,一般我们使用gl.drawArrays或者gl.drawElements都是将对象绘制在了默认的窗口中,但是当我们指定一个FrameBuffer时,再用这个两个方法去绘制,则会将对象会绘制于当前指定的FrameBuffer。

一般用于纹理的多重处理,比如对于一个纹理我们可以先灰度过滤,然后再模糊过滤,但是灰度过滤后还不能直接渲染到屏幕上,则可以使用FrameBuffer过渡,直到处理完成之后再渲染到屏幕上;
使用范例可以去WebGL简单实现高斯模糊这个例子中查看。

16、组合矩阵

在空间中存在一个三角形,红色坐标系为其自身坐标系

对三角形在绿色世界坐标系中做一些变换,该变换矩阵可以被称为模型矩阵

相机可以在某个位置以某种姿态观察这个三角形;蓝色坐标系为视图坐标系,其相对于世界坐标系的位置的变换矩阵为视图矩阵

三角形投影到相机时有很多种方式,分别对应不同的投影矩阵

这些变换矩阵共同决定了该三角形最终投影到屏幕的样子。

17、隐藏面消除

WebGL在默认情况下会按照缓冲区中的顺序绘制图形,而且后绘制的图形覆盖先绘制的图形,这种做法比较高效,如果场景中的对象以及观察者状态都不发生任何变化,这种做法没有任何问题;但是如果场景中的对象或者观察者状态发生了变化,那么有可能会影响对象的显示次序,这时候还按照默认顺序绘制图形就会有问题了,为了解决这个问题,WebGL提供了隐藏面消除功能,这个功能会自动根据对象和观察者的状态计算场景中对象的显示顺序;启动该机制只需要两行代码:

    //开启隐藏面消除功能
    gl.enable(gl.DEPTH_TEST);


    //开启隐藏面消除功能后,每次绘制之前,除了要清除颜色缓冲区还需要清除深度缓冲区
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

在绝大多数情况下,隐藏面消除功能都能很好的工作,然而当几何图形或者物体的两个表面极为接近时,就会出现新的问题,使得表面看上去斑斑驳驳,这种现象被称为深度冲突,主要原因在于两个面过于接近,深度缓冲区有限的精度已经不能区分哪个在前,哪个在后了。

针对上述情况WebGL提供一种被称为多边形偏移的机制来解决这个问题,该机制将自动在Z值上加上一个偏移量,偏移量的值由物体表面相对于观察者视线的角度来确定;启动该机制同样也只需要两行代码:

    //启动多边形偏移
    gl.enable(gl.POLYGON_OFFSET_FILL);


    //在绘制之前指定用来计算偏移量的参数
    gl.polygonOffset(1.0,1.0);

18、通过顶点索引绘制物体

通过gl.drawElements配合gl.ELEMENT_ARRAY_BUFFER可以实现通过顶点索引绘制物体,这种绘制方式相对于gl.drawArraysgl.ARRAY_BUFFER纯顶点绘制物体方式存在一定的优势,特别是当共享顶点很多时可以节约很多内存。

19、按行主序和按列主序

在编程时我们通常使用数组存储矩阵,但是矩阵是二维的,数组是一维的,所以如果在数组中按行存储矩阵元素就被称为按行主序,在数组中按列存储矩阵元素就被称为按列主序;

WebGL和OpenGL一样,矩阵元素是按列主序存储在数组中的。

20、光源类型

  • 平行光类似于自然中的太阳光,光线是相互平行的,可以用一个方向和一个颜色来定义;
  • 点光源类似于人造灯泡的光,光是从一个点向周围的所有方向发出的光,因此我们需要指定点光源的位置和颜色,光线的方向将根据点光源的位置和被照射之处的位置计算出来;
  • 环境光环境光是指那些经光源发出后,被墙壁等物体多次反射,然后照到物体表面上的光,环境光从各个角度照射物体,其强度都是一致的,环境光不需要指定位置和方向,只需要指定颜色即可。

除了上述三种基本类型的光,还有很多其它更加特殊的光源类型,可以参考《OpenGL ES 2.0 Programming Guide》。

21、反射类型

漫反射是指在粗糙的物体表面,反射光以不固定的角度反射出去,因此漫反射的反射光在各个方向上是均匀的。

漫反射光颜色 = 入射光颜色 * 表面基底色 * 入射角余弦;
环境反射光颜色 = 入射光颜色 * 表面基底色。

二、GLSL

1、attribute变量和uniform变量

都是GLSL ES中的变量类型,使用这两种变量可以把数据从JavaScript程序中传给着色器程序;

attribute和uniform类似都是存储限定符,主要区别如下:attribute只能用于顶点着色器,用来表示逐顶点数据;uniform可以用于顶点着色器也可以用于片元着色器,用来表示不变的数据。

uniform表示不变的数据的意思是在GLSL程序内部不能再被改变,但是可以再次由JavaScript程序赋予新的值。

2、varying变量以及颜色值内插

暂时理解为varying变量的作用是从顶点着色器向片元着色器传输数据;

颜色值内插暂时就理解为颜色自动渐变(如果想要深入了解内插过程,可以参考《计算机图形学》)。

示例程序

发表评论

电子邮件地址不会被公开。 必填项已用*标注