我们在接下来的学习中,我们主要会了解到一下一些知识点:
- 顶点缓冲区
- 索引缓冲区
- 纹理
- 帧缓冲
- 深度缓冲
- 颜色缓冲
- 模型缓冲
- 模型矩阵
- 观察矩阵
- 投影矩阵
- 视口
本节先介绍顶点缓冲区
顶点缓冲区的使用步骤:
- 创建缓冲区
- 绑定缓冲区到WebGL对象
- 传入数据到缓冲区
1 ,createBuffer()
triangleBuffer = webgl.createBuffer();
创建缓冲区,在显存上。
2,bindBuffer()
<span style="font-size:18px;">webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);</span>
将创建出来的缓冲区绑定到webGL对向上,这里我们使用了2个参数,第一个是类型,因为我们使用的是数组的点数据,所以使用系统提供的 gl.ARRAY_BUFFER
变量。第二个参数是我们需要绑定的Buffer对象,这里使用的就是我们前边创建出来的Buffer对象。
3,bufferData()
var jsArrayData =
[
0.0, 1.0, 0.0,//上顶点
-1.0, -1.0, 0.0,//左顶点
1.0, -1.0, 0.0
];//右顶点
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData), webgl.STATIC_DRAW);
这一步我们将我们的点位置数组传入到Buffer对象中 该方法接受的3个参数分别为
- 数据类型,可以是
gl.ARRAY_BUFFER
,gl.ELEMENT_ARRAY_BUFFER
,本例中使用的是gl.ARRAY_BUFFER
- 数据
- 缓冲类型,有这么几种可以供我们选择
GL_STREAM_DRAW
,GL_STATIC_DRAW
,GL_DYNAMIC_DRAW
GL_STREAM_DRAW
,缓冲区的数据不变用GL_STATIC_DRAW,缓冲区数据经常变动用GL_DYNAMIC_DRAW
于是问题就出现了,我的一部分数据不变,一部分数据经常变动,该如何处理?这个以后再谈
给缓冲区赋值还有另外一种方式,用到以下两个函数:
void bufferData(GLenum target,
GLsizeiptr size
, GLenum usage);
void bufferSubData(GLenum target, GLintptr offset, BufferDataSource? data);
其中第一个函数是我们之间用到的 void bufferData(GLenum target, BufferDataSource? data, GLenum usage)函数的重载。
步骤是:1,先创建一个size大小的空的缓冲区,2,再往里面传值(PS:以后开发推荐用这种方式,灵活)
//另外一种赋值方式
webgl.bufferData(webgl.ARRAY_BUFFER,4*9,webgl.STATIC_DRAW);
webgl.bufferSubData(webgl.ARRAY_BUFFER, 0, new Float32Array(jsArrayData));
4*9的意思是:绘制之间的三角形要3个点,一个点有3个坐标(x,y,z)共9个变量,由于是float类型的,占4个字节,共4*9个字
节,
即存储这9个变量要36个字节的缓冲区大小。
再解释下GLintptr offset的含义:offset单位是字节,意思是从BufferDataSource? data所指向的数组或缓冲的首地址偏移offset个字节开始读数据,上面代码offset是0,代表是从数组或缓冲区首地址开始读数据,若offset=2时,如下图所示:

上节提到了:
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer)
使用了两次,现在来详细的解释下原因。
我在创建一个缓冲区,代码如下:
jsArrayData1 = [
1.0, 1.0, 0.0,//上顶点
-1.0, -1.0, 0.0,//左顶点
0.0, -1.0, 0.0
];//右顶点
triangleBuffer1=webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer1);
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData1), webgl.STATIC_DRAW);
新的顶点数组jsArrayData1,新的缓冲区triangleBuffer1,注意bufferData()这个操作并没有指定是向哪个缓冲区传输数据,说明是上一个句代码bindBuffer()指定的,它指定的是一个状态,是一个状态机,当设置好后若不改变则会一直保持这个状态,现在绑定的是triangleBuffer1,则目前都是使用的是triangleBuffer1指向的缓冲区。
下面就是绘制了
webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer1);
webgl.enableVertexAttribArray(v3PositionIndex);
webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 0, 0);
webgl.drawArrays(webgl.TRIANGLES, 0, 3);
注意在绘制前又调用了一次bindBuffer(),绑定的是triangleBuffer1,使用是其绑定的缓冲区,绘制的结果是:
若改成:
webgl.bindBuffer(webgl.ARRAY_BUFFER,triangleBuffer)
则绘制的结果是:
bindBuffer()的作用是修改当前的状态。
下面来介绍下剩下来的几个函数:
void enableVertexAttribArray(GLuint index);
void vertexAttribPointer(GLuint indx, GLint size, GLenum type,
GLboolean normalized, GLsizei stride, GLintptr offset);
void drawArrays(GLenum mode, GLint first, GLsizei count);
void vertexAttribPointer(GLuint indx, GLint size, GLenum type,
GLboolean normalized, GLsizei stride, GLintptr offset);
void drawArrays(GLenum mode, GLint first, GLsizei count);
第一个enableVertexAttribArray()是启用顶点属性索引
第二个函数vertexAttribPointer()重点说一下,第一个参数GLuint indx是指shader程序的输入入口,由于如下操作
webgl.bindAttribLocation(programObject,v3PositionIndex,"v3Position");
v3PositionIndex索引与shader程序里面的v3Position变量绑定了,vertexAttribPointer()的含义是告诉显卡从当前绑定的缓冲区(bindBuffer()指定的缓冲区)中读取顶点数据,入口是v3PositionIndex,即传值给v3Position。
第二个参数GLint size是指v3Position的维度,由于类型是vec3,所以size = 3。第三个参数GLenum type是指参数类型,
指定一个数据占多少个字节。第四个参数GLboolean normalized是指是否标准化(化为0-1范围),第五个参数比较复杂,
先讲第六个参数GLintptr offset,这参数是指从shader程序的入口v3PositionIndex从bindBuffer()绑定的缓冲区偏移offset个字节
开始读数据,这里offset=0,指从绑定缓冲区首地址开始读。可以参照上边的bufferSubData()函数理解。
现在我们来看第五个参数GLsizei stride,stride的意思是步长,单位是字节,指的是每个顶点数据所占的字节数。
我们上一节例子的顶点数据只包含位置信息(xyz),其实还可以包含其他一些信息,如颜色信息(rgba),法向量(nxnynz),
纹理信息(uv)等。现在改写下顶点数据:
var jsArrayData =
[
//x y z nx ny nz
-0.5, +0.5, 0.0, 0.0, 0.0, 0.0,
+0.5, +0.5, 0.0, 0.0, 0.0, 0.0,
+0.5, -0.5, 0.0, 0.0, 0.0, 0.0,
-0.5, +0.5, 0.0, 0.0, 0.0, 0.0,
+0.5, -0.5, 0.0, 0.0, 0.0, 0.0,
-0.5, -0.5, 0.0, 0.0, 0.0, 0.0,
];//右顶点
一个顶点信息增加了法向量信息,而由于我们的shader程序中v3Position是顶点的位置数据,若stride=0时,
则最终
v3Position读取的数据为(-0.5,0.5,0.0),
(0.0,0.0,0.0),(0.5,0.5,0.0)为jsArrayData数组的前三个,
读的数据有误,
所以stride=6*4(每个数据占4个字节)才能保证读到我们想要的数据,stride=6*4的意思是每个步长24个字节
读取一个v3Position数据(前三个数据传给v3Position)。
第三个函数drawArrays(),第一个参数GLenum mode指的是绘制图元模式,下面是WebGL的图元模式:
const GLenum POINTS = 0x0000;
const GLenum LINES = 0x0001;
const GLenum LINE_LOOP = 0x0002;
const GLenum LINE_STRIP = 0x0003;
const GLenum TRIANGLES = 0x0004;
const GLenum TRIANGLE_STRIP = 0x0005;
const GLenum TRIANGLE_FAN = 0x0006;
const GLenum LINES = 0x0001;
const GLenum LINE_LOOP = 0x0002;
const GLenum LINE_STRIP = 0x0003;
const GLenum TRIANGLES = 0x0004;
const GLenum TRIANGLE_STRIP = 0x0005;
const GLenum TRIANGLE_FAN = 0x0006;
第二个参数GLint first指从绑定的缓冲区(bindBuffer())中首地址偏移first 下标(不是字节数)开始读数据。
第三个参数count是指绘制顶点数据的个数,即shader代码运行的次数。
下面简单介绍下WebGL的坐标系。
本节介绍的东西比较多,也比较啰嗦,若有错误,欢迎指正。
本节完!