写在前面
上一篇笔记中,我们认识了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个顶点的相关数据
图形装配(画点还是画线画三角)
将孤立的顶点坐标装配成几何图形,图形的类别由gl.drawArrays的第一个参数决定,比如gl.POINTS, gl.TRIANGLES
光栅化(确定哪些像素需要着色)
将装配好的几何图形转化为片元,将矢量的几何图形转变为栅格化的片元(像素)
执行片元着色器(着色)
对各个片元着色
回到第一步,读完了就退出,没完就读取下一个顶点再来一遍
所以顶点着色器是逐顶点执行的,片元着色器是逐片元执行的,就像一个双重循环,外层遍历顶点,内层遍历片元。以上过程如下图所示:
之前的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
包含上述代码的完整的例子,请查看:
红色渐变三角形:http://www.ayqy.net/temp/webgl/fragment-shader-detail/index.html
根据坐标设置片元颜色,验证片元着色器逐片元执行的过程
注意:片元着色器的内置变量gl_FragCoord
保存着片元的坐标信息(gl_FragCoord.x, gl_FragCoord.y)
,如果能够输出的话,就会发现片元着色器逐顶点执行的过程,但API没有提供输出信息到console的方法,我们可以根据片元坐标设置颜色来验证
五.总结
varying变量不仅能够实现渐变效果,还可以利用它内插出各种拿不到的数值,比如片元的世界坐标,片元在光源坐标系中的坐标等等,非常有用
我们现在已经可以绘制彩色图形了,与颜色有关的另一个特性是贴图(纹理映射),这是下一篇笔记的内容
参考资料
- 《WebGL编程指南》
很详细 谢谢您
想请教一下第二步图形装配的时候由于第一步中是取一个点,那第二步怎么装配成一个三角形