我的webgl学习之路(三)用webgl画线
我的webgl学习之路(三)用webgl画线
我们都知道两个点决定一条线段;那么我们怎么画两个点呢?甚至是多个点呢?用gl.vertexAttrib3f(a_Position,1.0,0.0,0.0)循环?实在太麻烦了;那么不得不说的缓冲区对象;
缓冲区:它是webgl提供的一种很方便的机制,它可以一次地向着色器传入多个顶点数据。缓冲区对象是webgl系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>多个点绘制—缓冲区</title>
<script id="vs" type="x-shader/x-vertex">
attribute vec4 a_Position;
void main(void){
gl_PointSize = 20.0;
gl_Position = a_Position;
}
</script>
<script id="fs" type="x-shader/x-fragment">
void main(void){
gl_FragColor =vec4(1.0,0.0,0.0,1.0);
}
</script>
</head>
<body>
<canvas id="canvas" style="background-color:black"></canvas>
<script>
onload =function () {
var canvas = document.getElementById("canvas");
canvas.width = 500;
canvas.height = 500;
var gl = canvas.getContext("webgl");
/*清空画板上的颜色,并初始化颜色*/
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//设定canvas初始化时候的深度
gl.clearDepth(1.0);
//清空画面上的颜色
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//顶点着色器和片段着色器生成
var v_shader= create_shader("vs");
var f_shader = create_shader("fs");
// 程序对象的生成和连接
var program= create_program(v_shader,f_shader);
//获取a_Position变量的存储位置
var a_Position= gl.getAttribLocation(program,"a_Position");
//创建缓冲区
var vertices= new Float32Array([
0.0,0.5,-0.5,
-0.5,0.5,-0.5
]);
var n = 3;//点的个数
//创建缓冲区
var vertexBuffer= gl.createBuffer();
if(!vertexBuffer){
console.log("Failed to createthe buffer object");//缓冲区创建失败
return -1;
}
//将缓冲区绑定到目标对象
gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);
//向缓冲区写入数据
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);
//将缓冲区对象分配给a_Position; 参数一:传入数据;参数二:指定每个顶点传入多少个数(2表示只取两个数传入,剩下的两个数0.0和1.0补上;1.0是透明度,跟vertexAttrib2f()类似)
gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);
//连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
//开始绘制,显示器显示结果;参数二:从哪个点开始绘制;参数三:绘制几个点
gl.drawArrays(gl.POINTS, 0, n);
functioncreate_program(v_shader, f_shader) {
varprogram = gl.createProgram();
gl.attachShader(program, v_shader);
gl.attachShader(program, f_shader);
gl.linkProgram(program);
gl.useProgram(program);
returnprogram;
}
functioncreate_shader(id) {
//用来保存着色器的变量
var shader;
//根据id从HTML中获取指定的script标签
var scriptElement = document.getElementById(id);
//如果指定的script标签不存在,则返回
if (!scriptElement) {
return;
}
// 判断script标签的type属性
switch (scriptElement.type){
// 顶点着色器的时候
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);//生成顶点着色器
break;
// 片段着色器的时候
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);//生成片元着色器
break;
default :
return;
}
//将标签中的代码分配给生成的着色器
gl.shaderSource(shader, scriptElement.text);
//编译着色器
gl.compileShader(shader);
//判断一下着色器是否编译成功
if (gl.getShaderParameter(shader,gl.COMPILE_STATUS)) {
// 编译成功,则返回着色器
return shader;
} else{
// 编译失败,弹出错误消息
alert(gl.getShaderInfoLog(shader));
}
}
functioncreate_program(vs, fs) {
//程序对象的生成
var program = gl.createProgram();
//向程序对象里分配着色器
gl.attachShader(program, vs);
gl.attachShader(program, fs);
//将着色器连接
gl.linkProgram(program);
//判断着色器的连接是否成功
if (gl.getProgramParameter(program,gl.LINK_STATUS)) {
// 成功的话,将程序对象设置为有效
gl.useProgram(program);
// 返回程序对象
return program;
} else{
// 如果失败,弹出错误信息
alert(gl.getProgramInfoLog(program));
}
}
}
</script>
</body>
</html>
相比上一节中,代码并没有多多少;gl_drawArrays(gl.POINTS,0,n);n为3,这里是我们要绘制点的个数;
使用缓冲区对象的流程:
1、创建缓冲区对象(gl.createBuffer());
2、把缓冲区对象(gl.bindBuffer())绑定到目标对象上(gl.ARRAY_BUFFER);
3、将数据写入缓冲区对象(gl.bufferData());
4、将缓冲区对象分配给attribute变量(gl.vertexAttribPointer());
5、开启attribute变量(gl.enableVertexAttribrray());
Vertices 是我们保存的顶点数据,我们并没有把z坐标传入;
流程如下图;
gl.bindBuffer(参数一,参数二);
参数一:它有两个基本的参数:gl.ARRAY_BUFFER :表示缓冲区对象中包含了顶点的数据;和gl.ELEMENT_ARRAY_BUFFER:表示缓冲区对象中包含了顶点索引(有些重复的点我们就可以用索引重复利用,减少数据过大),
参数二:需要绑定的缓冲区对象
gl.bufferData();
参数一:目标对象,gl.ARRAY_BUFFER或gl.ELEMENT_ARRAY_BUFFER
参数二:顶点数据,
参数三:绘制方式;gl.STATIC_DRAW:只会向缓冲区对象中写入一次数据,但需要绘制很多次;gl.STREAM_DRAW只会向缓冲区对象中写入数据,然后会绘制若干次;gl.DYNAMIC_DRAW会向缓冲区对象中多次写入数据,并绘制很多次;为什么会绘制很多次,那是因为它会对你传入的数据一个顶点一个的取然后绘制;
gl.vertexAttribPointer();
参数一:指定分配attribute变量的存储位置;
参数二:指定缓冲区中每个顶点的分量个数(1到4)。若size比attribute变量需要的分量数小,缺失分量将按照与gl.vertexAttrib[1234]f()相同的规则补齐;如:size为1,那么第2,3会自动补为0.0,第4为1.0
参数三:用一下类型之一来指定数据格式;无符号字节Uint8Array;gl.SHORT短整形;gl.UNSIGNED_SHORT无符号短整型;gl.INT整型;gl.UNSIGNED_INT无符号整型;gl.FLOAT浮点型;
参数四:true或false,表明是否将非浮点型的数据归一化到[-1,1]的区间,
参数五:指定相邻两个顶点间的字节数,默认为0;
参数六:从什么位置开始存储;
如果你想绘制线你可以把gl.drawArrays();参数一改成gl.LINES或者gl.LINE_STRIP、gl.LINE_LOOP试试效果是什么样的;
//LINES:线段;两个点绘制一条线段 //LINE_STRIP:一条连接的线;绘制方式(v0,v1),(v1,v2),(v2,v3) //LINE_LOOP:封闭的连线,起点和终点连接在一起;
它的整个执行如下图:
你可以试着改一些数据,让它变动,你就更加明白是怎么回事了;