上次咱们介绍了如何在<canvas>中使用WebGL,以及几个基础的WebGL函数;实现了背景色的重置;为了扩展方便,咱们把上次的代码作了些改动,将绘制图形的js独立成文件,这样咱们只关注与这个js文件的编写;之后除非HTML文件发生变化,咱们就跳过它,直接讨论JavaScript代码。html
1 <!doctype html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <meta name="Generator" content="EditPlus®">
6 <meta name="Author" content="Mirror">
7 <meta name="Keywords" content="">
8 <meta name="Description" content="">
9 <title>Hello Point</title>
10 <!--《WebGL编程指南》的做者为读者编写的WebGL辅助函数-->
11 <script src="lib/webgl-utils.js"></script>
12 <script src="lib/webgl-debug.js"></script>
13 <script src="lib/cuon-utils.js"></script>
14 <!--JavaScript文件,在<canvas>中绘制图形-->
15 <script src="lib/hello-point.js"></script>
16 </head>
17 <body onload="main()">
18 <!--定义<canvas>标签,经过width属性和height属性规定它是一片400×400的绘制区域-->
19 <canvas id="myCanvas" width="400" height="400">
20 <!--当浏览器不支持时,会直接忽略<canvas>标签,而直接显示下面这一行提示-->
21 Please use the browser supporting "canvas". 22 </canvas>
23 </body>
24 </html>
接下来,咱们在此基础上,绘制一个位于原点(0.0,0.0,0.0)处的10个像素大的红色的点。由于使用的是三维图形上下文,因此指定这个点时须要使用三维坐标。坐标系统后面介绍,这里只须要理解为原点位于<canvas>中心位置。效果如以下:java
实际上,咱们使用矩形而不是圆来绘制一个点,由于绘制矩形比绘制圆更快;就像在前一次中咱们以RGBA的形式指定了背景色同样,这里也须要一样的处理;在前面,咱们使用2d上下文来绘制了一个矩形;先指定了绘图颜色,而后进行绘制。你可能认为WebGL也差很少,不幸的是,没那么简单。WebGL依赖于一种新的称为着色器的绘图机制。着色器提供了灵活且强大的绘制二维或三维图形的方法,全部WebGL必须使用它。正由于强大,因此更复杂。web
要使用WebGL绘图,必须使用着色器,哪怕是一个点(矩形);着色器程序是以字符串的形式“嵌入”在JavaScript文件中,而且在程序开始运行前就已经设置好了。WebGL须要使用两种着色器:顶点着色器、片源着色器;下面分别介绍:编程
顶点着色器:顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。顶点是指二维或三维空间中的一个点,好比二维或三维图形的端点或交点。canvas
片元着色器:进行逐片元处理过程(如光源)的程序。片元是一个WebGL术语,你能够将其理解为像素(图像的单元)。数组
在后续,咱们会详细的学习着色器。简单的说,在三维场景中,仅仅用线条和颜色把图形画出来是远远不够的。你必须考虑如光线照上去后,或者观察者的视角发生变化时,对场景会有什么影响。着色器能够高度灵活的完成这些工做;提供各类渲染效果。这也就是如今制做的三维场景如此逼真的缘由。浏览器
上图显示了WebGL系统的执行流程;左侧为浏览器,首先执行JavaScript程序,调用了WebGL的相关方法,而后顶点着色器和片元着色器就会执行,顶点着色器指定绘制图形的位置和尺寸;片元着色器则指定绘制图形的颜色;而后在颜色缓冲区内进行绘制,这时就清空了绘图区,最后,颜色缓冲区中的内容就自动在浏览器的<canvas>标签上显示出来。ide
回到咱们今天的目标来,下面显示了hello-point.js的代码。函数
1 //顶点着色器程序 2 var VSHADER_SOURCE = 3 "void main() { \n" + 4 //设置坐标 5 "gl_Position = vec4(0.0, 0.0, 0.0, 1.0); \n" + 6 //设置尺寸 7 "gl_PointSize = 10.0; \n" + 8 "} \n"; 9 10 //片元着色器 11 var FSHADER_SOURCE = 12 "void main() {\n" + 13 //设置颜色 14 "gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + 15 "}\n"; 16 17 function main() { 18 //获取<canvas>标签。 19 var canvas = document.getElementById("myCanvas"); 20 //获取WebGL绘图上下文。 21 var gl = getWebGLContext(canvas); 22 //若是浏览器不支持WebGL则提示错误。 23 if (!gl) { 24 console.log("Failed to get the rendering context for WebGL."); 25 return; 26 } 27 28 //初始化着色器 29 if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 30 console.log("Faile to initialize shaders."); 31 return; 32 } 33 34 //设置<canvas>的背景色 35 gl.clearColor(0.0, 0.0, 0.0, 1.0); 36 37 //清空<canvas> 38 gl.clear(gl.COLOR_BUFFER_BIT); 39 40 //绘制一个点 41 gl.drawArrays(gl.POINTS, 0, 1); 42 43 }
这个文件包含三个部分,顶点着色器程序(GLSL ES 语言),片元着色器程序(GLSL ES 语言)和主程序(JavaScript语言)。着色器程序代码必须预先处理成单个字符串的形式,因此咱们用+号将多行字符串连成一个长字符串。每一行以\n结束,这是因为当着色器内部出错时,就能获取出错的行号,这对于检查源代码中的错误颇有帮助;可是,\n并非必须的。为了更容易维护,也能够把着色器代码写到单独的文件中(就像javaScript文件同样),而后经过javaScript程序从文件中读取出来加载。学习
根据程序流程,加载页面----->执行main()函数----->获取<canvas>标签----->获取绘图上下文;到这里,都跟以前的流程同样;接下来,会执行名为initShaders()的函数。这个函数是《WebGL编程指南》的做者专门写的一个辅助函数;该函数被定义在cuon.util.js中。这个函数的做用是对字符串形式的着色器进行初始化。咱们来看下这个函数的具体参数定义:
Web系统由两部分组成,即顶点着色器和片元着色器。在初始化着色器以前,顶点着色器和片元着色器都是空白的,咱们须要将字符串形式的着色器代码从JavaScript传给WebGL系统,并创建着色器,这就是initShaders()函数要作的事情。着色器运行在WebGL系统中,而不是JavaScript程序中。
initShaders()函数执行成功后,着色器被建立好了并随时可使用,顶点着色器将被首先执行,它对gl_Position变量和gl_PointSize变量进行赋值,并将它们传入片元着色器,而后片元着色器再执行。实际上,片元着色器接收到的是通过光栅化(将几何图形变为二维图像的过程)处理后的片元值;如今能够简单认为这两个变量从顶点着色器传入了片源着色器。
下面,咱们来看看着色器如何画出一个点(矩形),前面提到,咱们须要三个信息来画出这个点:位置,尺寸和颜色。位置和尺寸在顶点着色器中指定,和C语言同样,着色器程序必须包含一个main()函数。main()前面的关键字void表示这个函数不会有返回值;并且也不能为main()函数指定参数。着色器程序使用 = 操做符为变量赋值。gl_Position和gl_pointSize这两个变量内置在顶点着色器中,并且有着特殊的含义:前者表示顶点的位置,后者表示点的尺寸。gl_Position必须被赋值,不然着色器就没法正常工做;gl_PointSize并非必须的,若是不赋值,着色器会为其取默认值1.0。
和JavaScript不一样,GLSL ES是一种强类型语言,也就是说,开发者须要明确指出某个变量是某种类型;相似于Java和C、C#等。在这个程序中,出现了两种数据类型,float:表示浮点数;vec4:表示由4个浮点数组成的矢量(也称做向量)。并且着色器语言没有相似Java、C#语言的类型隐式转换的功能,因此这样写:gl_PointSize = 10;就会致使错误;由于gl_PointSize 须要一个float类型的值,而10是整型。另外一个内置变量gl_Position的类型为vec4,可是三维坐标只有3个数,即X,Y和Z轴的坐标值,这就须要某种方法将其转化为vec4类型的变量。着色器提供了内置函数vec4()能够完成这个事情。就如咱们代码中那样使用:gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 前3个份量对应X,Y,Z轴坐标值,那第4个份量1.0是怎么来的呢。由4个份量表示的坐标被称为齐次坐标,由于它可以提升处理三维数据的效率,因此在三维图形系统中被大量使用。当第4个份量为1.0时,这个齐次坐标就能够表示“前三个份量为坐标值”的点。
如前所述,片元能够当作是显示在屏幕上的像素。片元着色器和顶点着色器同样,也有一个main()函数。片元着色器将点的颜色赋值给gl_FragColor,该变量是片元着色器惟一的内置变量,它控制着像素在屏幕上的最终颜色。对这个内置变量赋值后,相应的像素就会以这个颜色值显示。和顶点着色器中顶点位置同样。颜色值也是vec4类型的,分别表示RGBA的4个份量。
创建了着色器后,咱们开始进行绘制操做,咱们使用gl.DrawArray()函数进行绘制。这个函数的功能很是强大,能够用来绘制各类图形,具体参数说明以下:
由于咱们绘制的是单独的点,因此设置第1个参数为gl.POINTS;设置第2个参数为0,表示从第1个顶点(虽然只有一个顶点)开始画起的,第3个参数为1,表示这个程序仅仅只画了1个点。当程序调用gl.DrawArray()函数时,顶点着色器将被执行count次,每次处理一个顶点,这时候顶点着色器开始执行内部的main()函数,而后设置位置和尺寸;执行完成后,片元着色器开始执行其main()函数,设置颜色;最后,一个红色的10个像素大的点就被绘制在了(0.0,0.0,0.0,1.0)处,也就是绘制区域的中心位置。
如今,咱们对顶点着色器和片元着色器的工做服方式有了大体的了解,只是有个问题,为什么(0.0,0.0,0.0,1.0)会绘制到了<canvas>的中心位置?下次咱们介绍坐标系统会解开这个谜题。