一.canvas 2D绘制矩形
用canvas 2D绘制一个矩形很容易,几行代码轻松搞定:
<!-- html -->
<h5>canvas2d</h5>
<canvas id="canvas2d" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
// js
// 获取元素引用
var canvas2d = document.getElementById('canvas2d');
// 获取2D context
var ctx = canvas2d.getContext('2d');
// 设置填充色
ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
// 绘制矩形块
ctx.fillRect(100, 100, 200, 200);
实际效果就是一个200x200px淡紫色矩形块,周围一圈宽度100px的透明环。从代码的角度来看,应该是:400x400px的透明canvas中心被涂上了一个不透明度为0.75的200x200px的紫色矩形块
从ctx.fillRect(100, 100, 200, 200);
可以看出canvas 2D的坐标系与屏幕坐标系一样,左上角是(0, 0)
,x轴向右为正,y轴向下为正
从ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
可以看出rgba()
与CSS一样,rgb值为0~255,a为0~1表示不透明度
二.WebGL绘制矩形
1.获取context
类似于canvas2d.getContext('2d')
,WebGL需要获取专用的context:
var webgl = document.getElementById('webgl');
var gl = webgl.getContext('webgl');
这里习惯把WebGL的context命名为gl
,是为了以后调用API时与OpenGL调用方式保持一致,嗯,就图个好看
2.清空canvas
类似于canvas 2D提供的clearRect
方法,WebGL也有橡皮擦一样的东西,而且更强大,可以指定擦除后的颜色:
// 指定清空canvas的颜色
// 参数是rgba,范围0.0~1.0
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清空canvas
// gl.COLOR_BUFFER_BIT颜色缓存,默认清空色rgba(0.0, 0.0, 0.0, 0.0) 透明黑色,通过gl.clearColor指定
// gl.DEPTH_BUFFER_BIT深度缓存,默认深度1.0,通过gl.clearDepth指定
// gl.STENCIL_BUFFER_BIT模板缓存,默认值0,通过gl.clearStencil()指定
gl.clear(gl.COLOR_BUFFER_BIT);
对于本例而言,清空canvas不是必须的,但一定要会用这种方法,不然就疯了,因为在canvas 2D中,如果要设置黑色背景,我们会这样做:
// 黑色背景
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
ctx.fillRect(0, 0, canvas2d.width, canvas2d.height);
同样的思路,画一个黑块铺满整个canvas,在WebGL中当然也可以,但会相当麻烦
3.绘制矩形
为了绘制一个矩形,我们需要先做8件事情:
编写着色器源程序
使用着色器语言(GLSL ES)编写着色器源程序,再以字符串形式传入WebGL系统内部编译执行:
// 0.着色器源程序 // 顶点着色器源程序 var vsSrc = 'void main() {' + 'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 设置坐标 'gl_PointSize = 200.0;' + // 设置尺寸 '}'; // 片元着色器源程序 var fsSrc = 'void main() {' + 'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 设置颜色 '}';
GLSL ES的语法和C语言比较像,具体语法规则在以后的笔记里再说
创建着色器对象
没什么好说的,游戏规则
// 1.创建着色器对象 var vs = gl.createShader(gl.VERTEX_SHADER); var fs = gl.createShader(gl.FRAGMENT_SHADER); // 检查创建结果 if (vs === null) { log('gl.createShader(gl.VERTEX_SHADER) failed'); } if (fs === null) { log('gl.createShader(gl.FRAGMENT_SHADER) failed'); }
填充源程序
有了着色器对象,就可以把着色器源程序塞进去了
// 2.填充源程序 gl.shaderSource(vs, vsSrc); gl.shaderSource(fs, fsSrc);
编译
有了源码,赶紧编译,看报不报错
// 3.编译 gl.compileShader(vs); gl.compileShader(fs); // 检查编译错误 if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { log('gl.compileShader(vs) failed'); log(gl.getShaderInfoLog(vs)); // 输出错误信息 } if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) { log('gl.compileShader(fs) failed'); log(gl.getShaderInfoLog(fs)); // 输出错误信息 }
创建程序对象
除了着色器对象,还需要程序对象,js与WebGL内部交流主要通过这个程序对象,先做一个出来
// 4.创建程序对象 var prog = gl.createProgram(); // 检查创建结果 if (prog === null) { log('gl.createProgram() failed'); }
注意:上面4步都是一个顶点XX和一个片元XX,而这一步只需要1个prog对象
为程序对象分配着色器
每个prog都需要2个着色器(1个顶点着色器,1个片元着色器),所以检查分配错误时要与2比较,看prog身上是不是绑了2个shader
// 5.为程序对象分配着色器 gl.attachShader(prog, vs); gl.attachShader(prog, fs); // 检查分配错误 if (gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) !== 2) { log('gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) failed'); }
连接程序对象
类似于C语言程序的compile -> link -> run
// 6.连接程序对象 gl.linkProgram(prog); // 检查连接错误 if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { log('gl.linkProgram(prog) failed'); log(gl.getProgramInfoLog(prog)); }
使用程序对象
不做错误检查,因为没有返回值
// 7.使用程序对象 gl.useProgram(prog);
至此,为绘制一个矩形做的准备工作就完成了
最后当然是draw点什么
// 绘制矩形(一个点,但点的尺寸略大)
gl.drawArrays(gl.POINTS, 0, 1);
三.DEMO
包含上述代码的完整的例子,请查看:http://www.ayqy.net/temp/webgl/hoho/index.html
四.WebGL与canvas 2D的差异
1.坐标系不同
在WebGL中,canvas的中心是(0, 0)
,x轴向右为正,y轴向上为正,z轴从屏幕射出指向脸为正(右手坐标系)
2.坐标值不同
WebGL中,整个画布是Rect(-1.0, 1.0, 2, 2)
,也就是说canvas左上角坐标是(-1.0, 1.0)
,右上角坐标为(1.0, 1.0)
,画布尺寸是2×2,如果canvas本身是400x400px的,那么WebGL中坐标值的单位就是200px
注意:对于非坐标值属性,比如上面着色器源程序中的gl_PointSize
,其单位仍然是px
3.rgb值不同
片元着色器中有用到色值,vec4(r, g, b, a)
,与CSS的rgba()
不同,WebGL中rgb值范围是0.0~1.0,而且必须是带小数的形式,因为vec4
构造函数只接受float
型参数,int
不会被隐式转换为float
4.颜色叠加规则不同
运行DEMO能够发现canvas 2D与Web GL画出的紫色矩形颜色不同,canvas 2D中我们先绘制了黑色背景,再画上了紫色矩形,紫色矩形有0.25的透明,最终显示的颜色是紫色与黑色的叠加。
而WebGL中,紫色矩形的颜色没有与黑色叠加(body背景色就是我们给紫色矩形指定的颜色,能清楚地看到WebGL中没有发生紫黑叠加)。尝试在紫色矩形背后(z坐标为-0.1)再绘制一个黑色矩形,发现颜色仍然没有叠加,这需要特别注意
五.总结
从API使用步骤上来看WebGL就是这么麻烦,画一个矩形都要写这么多代码,实际上我们只绘制了一个点(略大只的点),绘制矩形还要更麻烦些,需要用4个顶点确定2个三角片,再分别填色
至于顶点着色器,片元着色器是什么?有什么作用?需要一个稍微复杂一点的例子来说明,我们在以后的笔记里再解释
参考资料
- 《WebGL编程指南》
颜色没有叠加是因为你开启了深度检测
深度检测默认是disabled的,颜色/透明度叠加与深度检测没有直接关系。需要开启混合(blend)并指定混合函数
Demo已更新,补充了blend