如何绘制一个点?
上次讲到了如何清空绘图区,这次来讲解如何绘制一个点。我们将在位于远点(0.0, 0.0, 0.0)处绘制一个十个像素大的红色的点。因为WebGL绘制的是三维图行,所以我们需要指定这个点的三维坐标。
在canvas中,我们绘制一个矩形并填充颜色
ctx.fillStyle = 'rgba(0,0,255, 1.0)';
ctx.fillRect(120, 10, 150, 150);
你可能认为WebGL也差不多。 不幸的是,事情并没有这么简单。 WebGL依赖于一种新的称为着色器的绘图机制。
着色器提供了灵活且强大的绘图二维或三维图形的方法。 所有的WebGL程序必须使用它。着色器不仅强大,而且复杂。仅仅通过一条简单的指令是无法实现的。
什么是着色器?
GLSL
的中文意思是 OpenGL 着色语言,英文全称是 OpenGL Shading Language,它是用来在 OpenGL 编写着色器程序
的语言。
我们知道了 GLSL 是用来编写着色器程序的语言,那么新的问题来了,着色器程序是用来做什么的呢? 简单地说,着色器程序是在显卡(GPU)上运行的简短程序,代替了 GPU 固定渲染管线
的一部分,使 GPU 渲染过程中的某些部分允许开发者通过编程
进行控制。
用一句话来说:着色器程序允许我们通过编程来控制 GPU 的渲染。
那么 GPU 渲染过程中的哪些部分允许开发者控制呢?下图是对 WebGL 渲染管线的简单演示:
上图简单演示了 WebGL 对一个红色三角形的渲染过程,绿色部分为开发者可以通过编程控制的部分:
- JavaScript 程序
- 处理着色器需要的
顶点坐标
、法向量
、颜色
、纹理
等信息,并负责为着色器
提供这些数据,上图为了演示方便,只是提供了三角形顶点的位置数据。 - 顶点着色器
- 接收 JavaScript 传递过来的
顶点信息
,将顶点绘制到对应坐标。 - 图元装配阶段
- 将三个顶点装配成指定
图元类型
,上图采用的是三角形图元。 - 光栅化阶段 将三角形内部区域用空像素进行填充。
- 片元着色器 为三角形内部的像素填充颜色信息,上图为暗红色。
实际上,对顶点信息的变换操作既可以在 JavaScript
中进行,也可以在着色器程序
中进行。通常我们都是在 JavaScript
中生成一个包含了所有变换的最终变换矩阵,然后将该矩阵传递给着色器,利用 GPU 并行计算优势对所有顶点执行变换。
开始绘制一个点
1. 准备着色器代码
我们从着色器程序
开始入手,先用GLSL编写顶点着色器
和片元着色器
。
- 顶点着色器
顶点着色器的主要任务是告诉 GPU 在裁剪坐标系
的原点(也就是屏幕中心)画一个大小为 10 的点。
void main(){
//声明顶点位置
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
//声明待绘制的点的大小。
gl_PointSize = 10.0;
}
- 片元着色器
顶点着色器中的数据经过
图元装配
和光栅化
之后,来到了片元着色器
,在本例中,片元着色器的任务是通知 GPU 将光栅化后的像素渲染成红色,所以片元着色器要对内置变量gl_FragColor
(代表像素要填充的颜色)进行赋值。void main(){
//设置像素的填充颜色为红色。
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
这样,就写好了着色器的代码。
这些代码后续会进行解释,我们先来实现效果
2.绘制一个点
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body onload="main()">
<canvas id="canvas" height="400" width="400">
你的浏览器不支持WebGL,请更换新的浏览器
</canvas>
</body>
<script src="../lib/webgl-util.js" ></script>
<script src="../lib/webgl-debug.js" ></script>
<script src="../lib/cuon-utils.js" ></script>
<script>
//顶点着色器程序
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";
function main() {
//首先获取到canvas的dom对象
var canvas = document.getElementById("canvas");
//获取到WebGL的上下文
var gl = getWebGLContext(canvas);
//不支持WebGL的浏览器将打印一个错误,并结束代码运行
if (!gl) {
console.log("浏览器不支持WebGL");
return;
}
//初始化着色器
if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){
console.log("初始化着色器失败");
return;
}
//指定一个覆盖(清空)canvas的颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//执行清空
gl.clear(gl.COLOR_BUFFER_BIT);
//绘制一个点
gl.drawArrays(gl.POINTS,0,1);
}
</script>
</html>
看到这里是不是有点懵,为什么这么写,上面那些代码又是做什么的,下一章来仔细解释。