1 将非坐标数据传入顶点着色器
当执行gl.drawArrays()函数时,存储在缓冲区对象中的数据将按照其在缓冲区中的顺序依次传给对应的attribute变量。在顶点着色器中,我们将这两个attribute变量分别赋值给的gl_Position和gl_PointSize,就在指定的位置绘制出指定大小的点了。
1.1 gl.vertexAttribPointer()的步进和偏移参数
使用多个缓冲区对象向着色器传递多种数据,比较适合数据量不大的情况。当程序中的复杂三维图形具有成千上万个顶点时,维护所有的顶点数据是很困难的,例如:三维模型有1000个顶点。WebGL允许我们把顶点的坐标和尺寸数据打包到同一个缓冲区对象中,并通过某种机制分别访问缓冲区对象中不同种类的数据。可以将顶点的坐标和尺寸数据按照如下方式交错组织。
var verticesSizes = new Float32Array([
//顶点坐标和点的尺寸
0.0, 0.5, 10.0,//第一个点
-0.5, -0.5, 20.0,//第二一个点
0.5, -0.5, 30.0,//第三个点
])
gl.vertexAttribPointer()的函数规范:
gl.vertexAttribPointer(location,size,type,normalized,stride,offset); 将绑定到gl.ARRAY_BUFFER的缓冲区对象分配给由location指定的attribute变量 |
|
location | 指定待分配attribute变量的存储位置 |
size | 指定缓冲区中每个顶点的分量个数(1到4) |
type | 指定数据格式,例如:gl.FLOAT |
normalize | true或false。表明是否将非浮点型的数据归一化到[0,1]或[-1,1]区间 |
stride | 指定相邻两个顶点间的字节数,默认为0 |
offset | 指定缓冲区对象中的偏移量(以字节为单位),即attribute变量从缓冲区中的何处开始存储。如果是从起始位置开始,该参数应为0 |
var FSIZE = verticesSizes.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0);
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);
gl.enableVertexAttribArray(a_PointSize);
参数stride表示:在缓冲区对象中,单个顶点的所有数据(这里就是顶点的坐标和大小)的字节数,也就是相邻两个顶点间的距离,即步进参数。
参数offset表示当前考虑的数据项距离首个元素的距离,即偏移参数。在verticesSizes数组中,顶点的坐标数据是放在最前面的,所以offset应当为0。而顶点尺寸位于verticesSizes中,前两个是顶点坐标,后一个是顶点尺寸,因此offset应当设置为FSIZE*2。
在开启已被分配的缓冲区对象的a_PointSize变量之后,剩下的任务就只有调用gl.drawArrays()进行绘制操作了。
再次执行顶点着色器时,WebGL系统会根据stride和offset参数,从缓冲区中正确地抽取出数据,依次赋值给着色器中的各个attribute变量,并进行绘制。
size:指定缓冲区中每个顶点的分量个数。顶点坐标是2个参数(x,y);顶点大小只有1个参数。
代码源码如下:
//顶点着色器程序
var VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute float a_PointSize;
void main(){
gl_Position = a_Position;
gl_PointSize = a_PointSize;
}
`;
//片元着色器程序
var FSHADER_SOURCE = `
precision mediump float;
uniform vec4 u_FragColor;//uniform变量
void main(){
gl_FragColor = u_FragColor;//设置颜色
}`
function main() {
//获取<canvas>元素
var canvas = document.getElementById('myCanvas');
if (!canvas) {
console.log("failed to retrieve the canvas element");
return;
}
//获取WebGL的绘图上下文
var gl = canvas.getContext('webgl');
if (!gl) {
console.log("failed to get webgl context");
return;
}
//初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log("failed to initialize shaders");
return;
}
//*************设置顶点位置start***************/
var n = initVertexBuffers(gl);
//*************设置顶点位置end***************/
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
gl.uniform4f(u_FragColor, 1.0, 0.0, 0.0, 1.0);
//设置canvas的背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0);//黑色
//清除canvas
gl.clear(gl.COLOR_BUFFER_BIT);
//绘制三个点
gl.drawArrays(gl.POINTS, 0, n);
}
function initVertexBuffers(gl) {
var n = 3;//3个顶点
var vertices = new Float32Array([0.0, 0.5, 10.0, -0.5, -0.5, 20.0, 0.5, -0.5, 30.0]);
var FSIZE = vertices.BYTES_PER_ELEMENT;
//1.1 创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log("failed to create the buffer object");
return -1;
}
//1.2 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
//1.3 向缓冲区对象中写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
//2.1 获取顶点坐标着色器变量位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
//2.2 将缓冲区对象分配给a_Position
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0);
//2.3 连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
//3.1 获取顶点大小着色器变量位置
var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
//3.2 将缓冲区对象分配给a_PointSize
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);
//3.3 连接a_PointSize变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_PointSize);
return n;
}
1.2 修改颜色
我们使用一种新的varying变量向片元着色器中传入数据,实际上,varying变量的作用是从顶点着色器向片元着色器传输数据。
在顶点着色器中,我们声明了attribute变量a_Color用以接收颜色数据,然后声明了新的varying变量v_Color,该变量负责将颜色值传给片元着色器。
attribute vec4 a_Color;
varying vec4 v_Color;
将a_Color变量的值直接赋给之前声明的v_Color变量。
v_Color = a_Color;
那么片元着色器如何接收呢,很简单,只需要在片元着色器中也声明一个varying变量就可以了。
varying vec4 v_Color;
在WebGL中,如果顶点着色器和片元着色器中有类型和命名都相同的varying变量,那么顶点着色器赋给该变量的值就会被自动地传入片元着色器。
因此,顶点着色器赋给v_Color变量的值被传递给了片元着色器中的v_Color变量,然后片元着色器将v_Color赋值给gl_FragColor,这样每个顶点的颜色均会被修改。
gl_FragColo