uniform变量与片元着色器_WebGL笔记4

写在前面

上一篇笔记中,我们通过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.getAttribLocationgl.vertexAttrib3f,这里用的是gl.getUniformLocationgl.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编程指南》

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code