OpenGLES顶点缓冲VBO

VBO

Vertex Buffer object

为什么要用VBO

不使用VBO时,我们每次绘制( glDrawArrays )图形时都是从本地内存处获取顶点数据然后传输给OpenGL来绘制,这样就会频繁的操作CPU->GPU增大开销,从而降低效率。
使用VBO,我们就能把顶点数据缓存到GPU开辟的一段内存中,然后使用时不必再从本地获取,而是直接从显存中获取,这样就能提升绘制的效率。

创建VBO的主要步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1. 创建VBO得到vboId
int[] vbos = new int[1];
GLES20.glGenBuffers(1, vbos, 0);
vboId = vbos[0];

//2. 根据id绑定VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);

//3. 分配VBO需要的缓存大小
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertex.length * 4,null, GLES20. GL_STATIC_DRAW);

//4. 为VBO设置顶点数据的值
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);

//5. 解绑VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

使用VBO的主要步骤:

1
2
3
4
5
6
7
8
//1. 根据id绑定VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);

//2. 设置顶点数据
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, 0);

//3. 解绑VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

我使用绘制图片纹理的代码来进行改造为VBOOpenGLES 绘制图片纹理

改造的只有BitmapTexture这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;


//纹理 根据坐标系映射
public class BitmapTexture {


//顶点坐标
static float vertexData[] = { // in counterclockwise order:
-1f, -1f, 0.0f, // bottom left
1f, -1f, 0.0f, // bottom right
-1f, 1f, 0.0f, // top left
1f, 1f, 0.0f, // top right
};

//纹理坐标 对应顶点坐标 与之映射
static float textureData[] = { // in counterclockwise order:
0f, 1f, 0.0f, // bottom left
1f, 1f, 0.0f, // bottom right
0f, 0f, 0.0f, // top left
1f, 0f, 0.0f, // top right
};

//每一次取点的时候取几个点
static final int COORDS_PER_VERTEX = 3;

private final int vertexCount = vertexData.length / COORDS_PER_VERTEX;
//每一次取的总的点 大小
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex


private Context context;

//位置
private FloatBuffer vertexBuffer;
//纹理
private FloatBuffer textureBuffer;
private int program;
private int avPosition;
//纹理位置
private int afPosition;
//纹理id
private int textureId;
//vbo id
private int vboId;

public BitmapTexture(Context context) {
this.context = context;

vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);


textureBuffer = ByteBuffer.allocateDirect(textureData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureData);
textureBuffer.position(0);
}


public void onSurfaceCreated() {
String vertexSource = ShaderUtil.readRawTxt(context, R.raw.vertex_shader);
String fragmentSource = ShaderUtil.readRawTxt(context, R.raw.fragment_shader);
program = ShaderUtil.createProgram(vertexSource, fragmentSource);

if (program > 0) {
//获取顶点坐标字段
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
//获取纹理坐标字段
afPosition = GLES20.glGetAttribLocation(program, "af_Position");

//创建vbo
createVBO();

int[] textureIds = new int[1];
//创建纹理
GLES20.glGenTextures(1, textureIds, 0);
if (textureIds[0] == 0) {
return;
}
textureId = textureIds[0];
//绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//环绕(超出纹理坐标范围) (s==x t==y GL_REPEAT 重复)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//过滤(纹理像素映射到坐标点) (缩小、放大:GL_LINEAR线性)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.bg);

if (bitmap == null) {
return;
}
//设置纹理为2d图片
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
}
}

public void draw() {
//使用程序
GLES20.glUseProgram(program);
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glEnableVertexAttribArray(afPosition);


//直接设置
// 设置顶点位置值
// GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//设置纹理值
// GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);

//使用vbo设置
useVboDraw();

//绘制 GLES20.GL_TRIANGLE_STRIP:复用坐标
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(avPosition);
GLES20.glDisableVertexAttribArray(afPosition);

}

private void createVBO() {
//1. 创建VBO
int[] vbos = new int[1];
GLES20.glGenBuffers(vbos.length, vbos, 0);
vboId = vbos[0];
//2. 绑定VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
//3. 分配VBO需要的缓存大小
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + textureData.length * 4, null, GLES20.GL_STATIC_DRAW);
//4. 为VBO设置顶点数据的值
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, textureData.length * 4, textureBuffer);
//5. 解绑VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}

private void useVboDraw() {
//1. 绑定VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
//2. 设置顶点数据
GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, 0);
GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexData.length * 4);
//3. 解绑VBO
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
}
-------------The End-------------