写在前面
上一篇笔记中,我们通过attribute变量把鼠标点击处的坐标信息传递到了顶点着色器,实现了点哪里就在哪里画点的效果,但画出来的点都是同一种颜色,如果想要动态设置点的颜色,就需要uniform变量了,因为attribute变量只能用于顶点着色器,而颜色信息在片元着色器中
一.uniform变量与attribute变量
uniform与attribute类似,都是存储限定符,主要的区别如下:
attribute
只能用于顶点着色器,用来表示逐顶点的数据(每个顶点中该值不一样),比如前面用到的
attribute vec4 a_Position
uniform
可以用于顶点着色器,也可以用于片元着色器,用来表示一致的、不变的数据(每个顶点中该值都一样),比如以后纹理映射中要用到的
uniform Sampler2D u_Sampler
,还有我们即将使用的uniform vec4 u_FragColor
P.S.uniform与attribute还有其它区别,而且还有一个存储限定符是varying,这些内容都将在GLSL ES语法笔记中展开
二.设置点的颜色
和设置坐标类似,先声明uniform变量接收颜色数据,再传入色值,绘制出来即可
1.片元着色器
着色的过程在片元着色器中,我们需要修改片元着色器源程序:
// 片元着色器源程序
//!!! 需要声明浮点数精度,否则报错No precision specified for (float)
var fsSrc = 'precision mediump float;' +
'uniform vec4 u_FragColor;' +
'void main() {' +
'gl_FragColor = u_FragColor;' + // 设置颜色
'}';
用法和attribute变量完全一致,但需要特别注意的一点是:需要声明浮点数精度,否则片元着色器会编译失败,并报如下错误:
ERROR: 0:1: ” : No precision specified for (float)
2.设置颜色
类似于gl.getAttribLocation
和gl.vertexAttrib3f
,这里用的是gl.getUniformLocation
和gl.uniform4f
:
// 设置颜色
// 获取uniform变量的存储位置
var u_FragColor = gl.getUniformLocation(glUtil.program, 'u_FragColor');
var color = item.color;
// 把色值传递给uniform变量
gl.uniform4f(u_FragColor, color.r, color.g, color.b, color.a);
注意色值rgba都是0.0~1.0的,不在此区间的数值会被自动截断为0.0或者1.0,但不报错
3.绘制多个点
此处是直接循环gl.drawArrays(gl.POINTS, 0, 1);
实现的,方法不科学但能实现效果,更科学的方法需要配合buffer,这是下一篇笔记的内容,我们暂时还没有解锁
var arrPos = [];
webgl.addEventListener('click', function(e) {
// 转换坐标
var x = (e.offsetX - webgl.width / 2) / (webgl.width / 2);
var y = (webgl.height / 2 - e.offsetY) / (webgl.height / 2);
console.log(x, y);
// 随机颜色
var color = {
r: Math.random(),
g: Math.random(),
b: Math.random(),
a: Math.random()
};
// 记录坐标、颜色
arrPos.push({
x: x,
y: y,
color: color
});
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// 在记录的所有坐标处绘制点
arrPos.forEach(function(item) {
// 2.给attribute变量赋值
// 获取attribute变量的存储位置
var a_Position = gl.getAttribLocation(glUtil.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// 把顶点位置传递给attribute变量
gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
// 设置颜色
// 获取uniform变量的存储位置
var u_FragColor = gl.getUniformLocation(glUtil.program, 'u_FragColor');
var color = item.color;
// 把色值传递给uniform变量
gl.uniform4f(u_FragColor, color.r, color.g, color.b, color.a);
// 绘制点
gl.drawArrays(gl.POINTS, 0, 1);
});
});
没有新的东西,只是一个粗暴的循环,唯一需要注意的问题是循环之前的clear
,上一篇笔记提到了这个问题:绘制完成后,颜色缓冲区会被重置,其中的内容会丢失。从效果上来看,clear
是在每次绘制之前都设置了黑色背景,如果没有clear
,背景就会变成透明的(vec4(0.0, 0.0, 0.0, 0.0))
三.DEMO
包含上述代码的完整的例子,请查看:http://www.ayqy.net//temp/webgl/uniform/index.html
四.总结
我们已经实现了点点点点出灿烂星空的效果,不考虑性能的话,这样实现还是不错的,因为下一篇笔记要介绍的buffer看起来更麻烦,但好处还是有的,有了buffer就可以一次传递一组数据,以后画三角形,给三角形涂色、贴图都会很方便
循环draw a point毕竟不是长久之计,总不能用一个一个的点拼三角形吧,何况还有3D模型,buffer还是很有必要的
参考资料
- 《WebGL编程指南》