varying变量与内插_WebGL笔记7

写在前面

上一篇笔记中,我们认识了7种图元,改改参数就能画出不同的东西,但具体细节没有解释清楚,这要从着色器的工作原理说起,而varying变量作为顶点着色器和片元着色器之间的数据通道,也是不得不说的

一.varying变量

之前解释过2种着色器变量:attribute变量、uniform变量,而varying变量是最后一种着色器变量,比前2种稍复杂一点,因为有内插(interpolate)的过程

varying变量的作用是从顶点着色器向片元着色器传值,varying变量只能是float类型的,只要在片元着色器中也声明同名varying变量,顶点着色器赋给该变量的值就会自动传入片元着色器(在给片元着色器中的同名varying变量赋值之前,有内插的过程,1.0传过去不一定是1.0)

注意:顶点着色器中的varying变量值不是直接传递,会先进行内插,内插就像补间动画一样

二.内插(interpolate)

插值,缺少数据才需要插值,比如想要把一系列散点连成平滑曲线,相邻已知点之间缺少很多点,此时就需要通过内插填补缺少的数据,最终平滑曲线上除已知点之外的所有点都是插值得到的

例如Photoshop的自定义渐变,我们只需要设置几个点的颜色就能自动生成一整条渐变带,这几个点之间的颜色都是通过内置插值算法得到的

varying变量的值传递到片元着色器之前进行的插值过程被称为内插,同样,我们也可以利用内插生成渐变

三.渐变三角形

只要片元着色器中声明与顶点着色器varying变量同名的varying变量,值就会自动传递过去(当然,有内插的过程)

1.着色器源程序

在片元着色器中声明同名varying变量,具体如下:

// 顶点着色器源程序
var vsSrc = 'attribute vec4 a_Position;' +
    'attribute vec4 a_Color;' +
    'varying vec4 v_Color;' +       // 声明varying变量
    'void main() {' +
    'gl_Position = a_Position;' +   // 设置坐标
    'gl_PointSize = 7.0;' +         // 设置尺寸
    'v_Color = a_Color;' +          // 给varying变量赋值
'}';
// 片元着色器源程序
//!!! 需要声明浮点数精度,否则报错No precision specified for (float) 
var fsSrc = 'precision mediump float;' +
    'varying vec4 v_Color;' +   // 声明同名varying变量
    'void main() {' +
    'gl_FragColor = v_Color;' + // 设置颜色
'}';

注意:无法直接给varying变量赋值,需要借助attribute变量接受外部值,再在顶点着色器内部给varying变量赋值

2.纯色三角形与渐变三角形

关键是三个顶点颜色是否一致,因为实际过程是:

  1. 读取顶点信息(坐标、颜色等等)

    执行顶点着色器,读取1个顶点的相关数据

  2. 图形装配(画点还是画线画三角)

    将孤立的顶点坐标装配成几何图形,图形的类别由gl.drawArrays的第一个参数决定,比如gl.POINTS, gl.TRIANGLES

  3. 光栅化(确定哪些像素需要着色)

    将装配好的几何图形转化为片元,将矢量的几何图形转变为栅格化的片元(像素)

  4. 执行片元着色器(着色)

    对各个片元着色

  5. 回到第一步,读完了就退出,没完就读取下一个顶点再来一遍

所以顶点着色器是逐顶点执行的,片元着色器是逐片元执行的,就像一个双重循环,外层遍历顶点,内层遍历片元。以上过程如下图所示:

webgl-vertex-shader-execution

webgl-vertex-shader-execution

webgl-varying

webgl-varying

之前的DEMO中颜色都是在片元着色器中写死的,比如gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75),所以画出的所有东西都是纯色的,但更科学的方式是把顶点颜色传入顶点着色器,根据顶点颜色内插出每个片元的颜色。比如画一条孤立线段,2个同色顶点之间的片元内插后都是该色,2个不同色顶点之间的片元内插后就出现其它颜色了

3.绘制渐变三角形

把3个顶点设置成不同颜色,如下:

var arrVtx = new Float32Array([
    // x, y, r, g, b
    -0.5, 0.5, 1.0, 0.0, 1.0, 1.0,  // 红色
    0.5, 0.5, 0.0, 1.0, 0.0, 1.0,   // 绿色
    -0.5, -0.5, 0.0, 0.0, 1.0, 1.0  // 蓝色
]);
//...顶点数组写入缓冲区
// 绘制点
gl.drawArrays(gl.POINTS, 0, arrVtx.length / 6);
setTimeout(function() {
    gl.clear(gl.COLOR_BUFFER_BIT);
    // 绘制三角形
    gl.drawArrays(gl.TRIANGLES, 0, arrVtx.length / 6);
}, 2000);

我们先绘制3个孤立点(红绿蓝),2s后绘制三角形,渐变三角形就出现了,这也说明了设置gl_PointSize只在draw point时有效,绘制三角形会自动忽略该值

四.DEMO

包含上述代码的完整的例子,请查看:

注意:片元着色器的内置变量gl_FragCoord保存着片元的坐标信息(gl_FragCoord.x, gl_FragCoord.y),如果能够输出的话,就会发现片元着色器逐顶点执行的过程,但API没有提供输出信息到console的方法,我们可以根据片元坐标设置颜色来验证

五.总结

varying变量不仅能够实现渐变效果,还可以利用它内插出各种拿不到的数值,比如片元的世界坐标,片元在光源坐标系中的坐标等等,非常有用

我们现在已经可以绘制彩色图形了,与颜色有关的另一个特性是贴图(纹理映射),这是下一篇笔记的内容

参考资料

  • 《WebGL编程指南》

varying变量与内插_WebGL笔记7》上有2条评论

  1. 给点阳光yh

    想请教一下第二步图形装配的时候由于第一步中是取一个点,那第二步怎么装配成一个三角形

    回复

发表评论

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

*

code