本节介绍如何使用VAO和VBO进行图形绘制。
先看效果:
本系列文章关注的重点是游戏引擎的开发,关于OpenGL的基础知识,这里就不再详细讲了,否则篇幅篇幅就太长了。这里的相关概念我只简单讲下我自己的理解,如果各位不了解话,请查阅相关的OpenGL书籍。
VBO:顶点缓存对象,其中存储着顶点的相关信息(包括:顶点坐标、顶点颜色、顶点法线、纹理坐标等,这些也就是常说的顶点属性)。每个VBO只能储存一个顶点属性,如:保存顶点位置的VBO,保存顶点颜色的VBO…
VAO:顶点数组对象,内部维护多个顶点属性列表,而顶点属性存储在VBO中,即VAO中存储着VBO中有关顶点属性配置的信息。
代码部分:
创建RawModel类,存储实体数据
创建Loader类,加载实体数据
创建ShaderProgram类,编译、链接着色器
创建Render类,渲染实体
// RawModel.java
// 主要准备要绘制的实体对应的VAO和顶点数目,用来绘制。VAO中存储着各顶点属性的配置。让opengl可以按照VAO的配置解析传递的顶点数据。
package models;
public class RawModel {
private int vaoID;
private int vertexCount;
public RawModel(int vaoID,int vertexCount){
this.vaoID = vaoID;
this.vertexCount = vertexCount;
}
public int getVaoID() {
return vaoID;
}
public void setVaoID(int vaoID) {
this.vaoID = vaoID;
}
public int getVertexCount() {
return vertexCount;
}
}
// Loader.java
// 主要用来加载实体数据到VAO,即主要对实体数据进行相关配置, 最有返回一个RawModel对象,保存着配置好的VAO用于绘制。
package renderEngine;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import models.RawModel;
/**
* 加载VBO
*/
public class Loader {
private List<Integer> vaos = new ArrayList<Integer>();
private List<Integer> vbos = new ArrayList<Integer>();
public RawModel loadToVAO(float[] positions){
int vaoID = createVAO();
//配置vao
storeDataInAttributesList(0, 3,positions);
unbindVAO();
return new RawModel(vaoID, positions.length/3);
}
public void cleanUp(){
for(int vao:vaos){
GL30.glDeleteVertexArrays(vao);
}
for(int vbo:vbos){
GL15.glDeleteBuffers(vbo);
}
}
private int createVAO(){
int vaoID = GL30.glGenVertexArrays();
vaos.add(vaoID);
GL30.glBindVertexArray(vaoID);
return vaoID;
}
private void storeDataInAttributesList(int attributeNumber,int coordinateSize,float[] data){
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
FloatBuffer buffer = storeDataInFloatBuffer(data);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(attributeNumber, coordinateSize, GL11.GL_FLOAT, false,0,0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
private void unbindVAO(){
GL30.glBindVertexArray(0);
}
//将int[]转换为IntBuffer
private IntBuffer storeDataInIntBuffer(int[] data){
IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
//将float[]转换为FloatBuffer
private FloatBuffer storeDataInFloatBuffer(float[]data){
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
}
// Renderer.java
// 根据配置好的VAO进行绘制。
package renderEngine;
import models.RawModel;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
public class Renderer {
public void prepare(){
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
}
public void render(RawModel model){
GL30.glBindVertexArray(model.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, model.getVertexCount());
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
}
// MainGameLoop.java
// 测试代码
package engineTester;
import models.RawModel;
import org.lwjgl.opengl.Display;
import renderEngine.DisplayManager;
import renderEngine.Loader;
import renderEngine.Renderer;
public class MainGameLoop {
public static void main(String[] args) {
DisplayManager.createDisplay();
Loader loader = new Loader();
float[] positions = {
-0.5f,0.5f,0.0f,
-0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.5f,0.5f,0.0f,
-0.5f,0.5f,0.0f,
};
RawModel model = loader.loadToVAO(positions);
Renderer render = new Renderer();
render.render(model);
while(!Display.isCloseRequested()){
render.prepare();
// game logic
// render
render.render(model);
DisplayManager.updateDisplay();
}
loader.cleanUp();
DisplayManager.closeDisplay();
}
}
ok. 结束。
下篇预告:使用索引缓存。