varying变量和颜色插值
前面课程讲解过一系列顶点通过顶点着色器逐顶点处理后,再经过图元装配、光栅化环节会得到原始未定义颜色的片元,然后经过片元着色器逐片元添加颜色,会得到一副图像。在片元着色器程序中编写代码
代码案例
源码下载效果:
颜色线性插值
执行下面的代码你可以发现一个渐变色直线,由蓝色经过逐渐变为红色。它的实现过程很简单,前面的课程讲过定义两个顶点,绘制模式使用gl.LINES,连接两点形成一条直线,在片元着色器中再添加颜色。 下面的程序是分别定义两个顶点颜色的RGBA值对应两个顶点的位置坐标值,也就是说一个点一个位置数据一个颜色数据。点1为蓝色(0,0,1),点2位红色(1,0,0),R和B的值随着像素位置线性变化,R从0到1,B从1到0, 绿色成分G从0到0渐变,相当于没变化。相邻像素之间的同一种单色成分的差值是定值,所有像素的同一种单色成分是一个等差数列,给出两个点的像素值,GPU自动内插出两点之间所有像素的值,这个过程是像素的线性插值过程,
着色器程序
下面的顶点着色器和前面的相比就是增加了顶点颜色数据的处理,第13行代码使用关键字attribute声明了一个变量,用来接收第40行的顶点位置数据;第14行代码同样是使用关键字attribute声明了一个变量, 用来接收第41行的顶点颜色RGB值数据,同门的数据类型都是vec4,从这里也可以看出GLSL ES语言关键字attribute的作用就是声明一个可以接收顶面坐标、顶点颜色等等点数据的变量。
顶点着色器程序的使用关键字varying声明了一个v_color变量,这里你可能会想为什么不使用attribute关键字而是varying。使用attribute关键字声明的顶点坐标数据经过第17行处理,
经过装配、光栅化后得到一系列未定义颜色的片元,执行第18行代码
注意:片元着色器程序的第23行都是使用关键字varying声明了一个v_color变量,和顶点着色器中第14行代码形式一样,都是
注意:第22行代码是为了定义片元着色器中的所有浮点型float数据的精度,在着色器语言中lowp是精度限定字表示低精度。计算机资源有限,设置数据精度是为了提高执行效率。除了片元着色器中的浮点类型float数据,其它所有类型数据浏览器都有默认的精度,因此要设定片元着色器中浮点类型float数据精度,否则会报错,初学WebGL可以先不深入学习着色器语言,知道一个大概,先把WebGL的API对应的GPU渲染管线搞明白,再去详细研究着色器语言的语法。
11 // 顶点着色器源码 12 var vertexShaderSource = '' + 13 'attribute vec4 apos;'+ //顶点坐标变量 14 'attribute vec4 a_color;' +//attribute声明顶点颜色变量 15 'varying vec4 v_color;'+//varying声明顶点颜色插值后变量 16 'void main(){' + 17 'gl_Position = apos;' +//顶点坐标apos赋值给内置变量gl_Position 18 'v_color = a_color;' +//顶点颜色插值计算 19 '}'; 20 // 片元着色器源码 21 var fragShaderSource = '' + 22 'precision lowp float;' +//所有float类型数据的精度是lowp 23 'varying vec4 v_color;'+//接收顶点着色器中v_color数据 24 'void main(){' + 25 ' gl_FragColor = v_color;' +//插值后颜色数据赋值给对应的片元 26 '}';
获取顶点变量
程序执行完第65行的初始化函数initShader()后,会返回一个program对象,该对象包含顶点着色器中attribute关键字声明的顶点位置、颜色变量。调用getAttribLocation()方法,把program对象作为方法第一个参数,位置变量apos或颜色变量a_color字符串形式作为第二个参数可以返回对应顶点数据变量的位置。
30 /** 31 从program对象获取顶点位置变量apos、颜色变量a_color 32 **/ 33 var aposLocation = gl.getAttribLocation(program,'apos'); 34 var a_color = gl.getAttribLocation(program,'a_color');
设置顶点颜色、位置数据
通过第42~57行代码把数据传递给顶点着色器颜色和位置变量,位置数据只设置了x和y,z未设定默认为1,颜色数据设置了RGB,没有设置A,默认为1,注意第48行代码和第56行代码传递数据方式的差异。
36 /** 37 创建顶点位置数据数组data,存储两个顶点(-0.5,0.5、(0.5,0.5) 38 创建顶点颜色数组colorData,存储两个顶点对应RGB颜色值(0,0,1)、(1,0,0) 39 **/ 40 var data=new Float32Array([-0.5,0.5,0.5,0.5]); 41 var colorData = new Float32Array([0,0,1,1,0,0]);
创建缓冲区顶点
顶点颜色数据和顶点位置数据的数据类型一样,所以创建缓冲区、传入数据的方式基本一样。
42 /** 43 创建缓冲区colorBuffer,传入顶点颜色数据colorData 44 **/ 45 var colorBuffer=gl.createBuffer(); 46 gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer); 47 gl.bufferData(gl.ARRAY_BUFFER,colorData,gl.STATIC_DRAW); 48 gl.vertexAttribPointer(a_color,3,gl.FLOAT,false,0,0); 49 gl.enableVertexAttribArray(a_color); 50 /** 51 创建缓冲区buffer,传入顶点位置数据data 52 **/ 53 var buffer=gl.createBuffer(); 54 gl.bindBuffer(gl.ARRAY_BUFFER,buffer); 55 gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW); 56 gl.vertexAttribPointer(aposLocation,2,gl.FLOAT,false,0,0); 57 gl.enableVertexAttribArray(aposLocation);
课后练习案例
1.创建一个彩色三角形
源码下载在原来的基础上更改两处代码即可,两个顶点改为三个顶点,绘制模式从直线改为三角形,绘制点数从两个改为3个,这里你会发现,写一个WebGL程序好像很复杂很麻烦,但是改代码实现一个新的效果却不是太麻烦。这主要是程序要同时在CPU和GPU两个硬件中执行,需要一些CPU和GPU通信的Javascript API,除了CPU执行的Javascript语言,还有GPU执行的着色器程序,着色器程序又有多个不同的处理单元,还要分别编写着色器程序,只要你花一定时间把GPU的整个流水线搞明白一切问题迎刃而解,可以利用Javascript语言封装原生的WebGL API,实现代码复用。
/** 创建顶点位置数据数组data,存储3个顶点(-0.5,0.5、(0.5,0.5)、(0.5,-0.5) 创建顶点颜色数组colorData,存储3个顶点对应RGB颜色值(1,0,0)、(0,1,0)、(0,0,1) **/ var data=new Float32Array([-0.5,0.5,0.5,0.5,0.5,-0.5]); var colorData = new Float32Array([1,0,0,0,1,0,0,0,1]);
/**执行绘制命令**/ gl.drawArrays(gl.TRIANGLES,0,3);
2.两个单色三角面(不同颜色)
源码下载/** 创建顶点位置数据数组data,存储6个顶点 创建顶点颜色数组colorData,存储6个顶点对应RGB颜色值 **/ var data=new Float32Array([ -0.5,0.5,0.5,0.5,0.5,-0.5,//第一个三角形的三个点 -0.5,0.5,0.5,-0.5,-0.5,-0.5//第二个三角形的三个点 ]); var colorData = new Float32Array([ 1,0,0,1,0,0,1,0,0,//三个红色点 0,0,1,0,0,1,0,0,1//三个蓝色点 ]);
3.颜色插值(顶点位置、颜色使用一个缓冲区存储)
源码下载把顶点位置数据、顶点颜色数据存储在同一个数组中,然后存入一个缓冲区中。在数组中的顶点颜色、位置坐标数据要交叉排列,使用方法vertexAttribPointer()才能合理选择数据,交叉排列正是因为vertexAttribPointer()方法5个参数的特点决定的。
/** 创建顶点位置数据数组data,存储两个顶点(-0.5,0.5、(0.5,0.5) 存储两个顶点对应RGB颜色值(0,0,1)、(1,0,0) **/ var data=new Float32Array([ -0.5,0.5, 0,0,1, 0.5,0.5, 1,0,0 ]); /** 创建缓冲区buffer,传入顶点颜色、位置数据data **/ var buffer=gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,buffer); gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW); //4表示data数组一个元素占据的字节数 //倒数第二个参数4*5表示每5个元素是一个选择单元 //第2个参数2表示从5元素组成的一个选择单元中选择前2个作为顶点位置数据 gl.vertexAttribPointer(aposLocation,2,gl.FLOAT,false,4*5,0); //最后一个参数4*2表示5元素组成的一个选择单元中偏移2个元素 //第2个参数3表示从5元素组成的一个选择单元中选择后三个作为顶点颜色数据 gl.vertexAttribPointer(a_color,3,gl.FLOAT,false,4*5,4*2); gl.enableVertexAttribArray(aposLocation); gl.enableVertexAttribArray(a_color);