【WebGL】绘制一个点
发表于2018-05-03
在之前的学习中,我们知道了怎么样使用一些简单的WebGL相关函数,现在更进一步来绘制最简单的图形:一个点。
绘制一个点远不是调用一个”draw point”
在之前的学习中,比如填充颜色可以用到
ctx.fillStyle = "rgba(0, 255, 255, 1.0)";
但是,是否绘制一个点也这么简单呢?比如调用一个drawPoint方法就行了。答案是否定的!
WebGL依赖于一种称为着色器(shader)的绘图机制。着色器提供了大量的二维或三维图形的方法,所有WebGL程序必须使用它。着色器不仅强大而且更复杂,所以仅仅通过一条简单的绘图命令是不能操作它的。
所以着色器是WebGL中一个非常重要的核心机制,我们将在之后的学习中一直伴随着循序渐进的学习它。
构建HelloPoint_1工程
首先,构建HelloPoint_1.html文件,这个文件很简单,和之前的几乎一样。直接列出代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello Point 1</title> </head> <body onload = "main()"> <canvas id = "webgl" width = "400" height = "400"> please use the browser supporting "canvas" </canvas> <script src = "../lib/webgl-utils.js"></script> <script src = "../lib/webgl-debug.js"></script> <script src = "../lib/cuon-utils.js"></script> <script src = "HelloPoint_1.js"></script> </body> </html>
然后,创建HelloPoint_1.js文件:
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, 1);\n'+ '}\n'; function main(){ var canvas = document.getElementById("webgl"); var gl = getWebGLContext(canvas); if(!gl){ return; } if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)){ console.log("failed to init shaders"); return; } gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.POINTS, 0, 1); }
可以看到,这里的js文件中的内容和我们之前创建的就存在了较大区别。从代码段的第1行到第10行多了一些我们暂时还不知道是啥的东西。接下来我们就看下这10行代码中的到底是什么神奇的东西。
着色器是什么?
HelloPoint_1.js是我们使用着色器的第一个WebGL程序。
可以看到,1~10行的着色器程序是以字符串的形式嵌入在js文件中的。在程序开始前就已经设置好了。
WebGL需要两种着色器,见第1行和第7行,分别对应如下:
- 顶点着色器(Vertex shader):顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。顶点指的是二维或者三维空间中的一个点。
- 片元着色器(Fragment shader):进行逐片元处理过程(如光照)的程序。片元(Fragment)是一个WebGL术语,可以理解为像素。
ps:在场景中,仅仅用线条和颜色把图形画出来是不够的。我们必须考虑:光照、视角等对场景的影响。着色器可以高度灵活的完成这些工作,提供各种渲染效果。
回到上面的例子,上面的程序的任务是在屏幕上绘制一个10像素大小的点,它用到两个着色器:
- 顶点着色器 — 指定了点的位置和尺寸
- 片元着色器 — 指定了点的颜色
主程序的执行流程
在细说示例代码中顶点和片段着色器之前,我们先来看下主程序main()的执行顺序。
可以看到这里,相较于之前一次学习的顺序,这里多了“初始化着色器”和“绘图”两个步骤:
初始化着色器 — 利用initShaders()对字符串形式的着色器进行了初始化。initShaders()函数说明如下图:
下图显示了initShaders()的执行效果:
可以看到这里先执行了顶点着色器,它对gl_Position和gl_PointSize变量进行了赋值,并将它们传递给片元着色器。再执行片元着色器。
ps:这里要注意的是,WebGL程序包括运行在浏览器中的JavaScript和运行在WebGL系统的着色器程序两个部分。
示例中的顶点和片元着色器程序
顶点着色器
在示例代码的第1行~第5行是一个顶点着色程序:
var VSHADER_SOURCE = 'void main(){\n' + 'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + 'gl_PointSize = 10.0;\n' + '}\n';
可以看到,和C语言一样,在顶点着色器中必须有一个main()函数。
然后将点的位置赋值给gl_Position变量,点的大小赋值给gl_PointSize变量。这两个变量都是WebGL的顶点着色器内置的。
ps:在顶点着色器中gl_Position必须要被赋值,gl_PointSize不是必须的(不赋值的话默认为1.0)。
下面来看下这两个内置变量的定义:
可以看到,这里比较特殊的是gl_Position,他是一个vec4类型的变量。在GLSL ES中,vec4是由四个浮点数组成的矢量。如下图:
另外需要注意的是:gl_PointSize是float类型的变量,这里赋值一定要是浮点数,不能是其他类型(和其他语言不通,内部不会做转换)。
最后,顶点着色器程序最后以字符串的形式存储在VSHADER_SOURCE变量中。
片元着色器
之前我们说到,片元可以理解为是一个像素。严格意义上来说:片元包含了这个像素的位置、颜色和其他信息。
片元着色器的作用是:处理片元,使其显示在屏幕上。
示例代码的第7行~第10行是一个片元着色器
var FSHADER_SOURCE = 'void main(){\n' + 'gl_FragColor = vec4(1, 0, 0, 1);\n'+ '}\n';
像顶点着色器一样,片元着色器也包含了一个main()函数。
然后对内置变量gl_FragColor进行赋值。
ps:gl_FragColor是片元着色器唯一的内置变量,它控制像素在屏幕上显示的颜色。
gl_FragColor也是一个vec4类型的变量。它的四个分量分别代表了颜色的RGBA值。
最后片元着色器程序以字符串的形式存储在FSHADER_SOURCE变量中。
绘制操作
在建立了着色器之后,我们就需要进行绘制操作。这里的示例代码就是绘制一个点。
和之前的程序一样,首先要清空绘制区域。
然后使用gl.drawArrays()来进行绘制。如29行代码:
gl.drawArrays(gl.POINTS, 0, 1);
gl.drawArrays()是一个很强大的函数,具体如下:
每个参数的说明如上所示。注意:第二个参数的索引值是从0开始的。
当程序调用gl.drawArrays()时,顶点着色器将被执行count次(第三个参数),每次处理一个顶点。当顶点程序执行完后,片元着色器就会开始执行。如下图:
根据这样的顺序,最终就能在屏幕上绘制出一个点,如下图:
来自:https://blog.csdn.net/xxwlzfb/article/details/52755356