我正在使用 LWJGL 创建某种 Minecraft 克隆。
问题是我只能获得大约 20 FPS。 (每个方向约32个街区) 另外,当我更改 block 时, View 会卡住几毫秒。
我正在使用 Slick 加载纹理。为了渲染 block ,我使用 VBO 作为纹理和顶点(没有 VAO、着色器或法线)。每个 block 由 6 个四边形组成,其中由 2 个三角形组成。 block 之间的面不会被渲染。每一帧渲染线程都会检查所有 32 * 32 block 是否已加载到缓冲区中以及是否应该加载(取决于玩家位置)。如果需要,则会创建 vbo 并将句柄保存到列表中。如果 block 太远,缓冲区就会被删除。渲染时,当前在显卡内存中的所有 vbo(句柄保存在列表中)都会被绘制。
如何加快渲染速度?我应该让缓冲区始终处于“加载”状态并只绘制所需的对象吗?法线、VAO 或着色器可以帮助我获得巨大的改进吗?不过,如果我获得更高的帧速率,将会有更多的 block ,而不仅仅是一个盘子,我计划导入 .obj。
Minecraft 在我认为的 DisplayLists 中呈现。这会是一个解决方案吗?为每个 block 或 block 创建一个显示列表,并在 block 或 block 发生更改时重新编译它们?
编辑:我用 VisualVM 测量发现最耗时的方法是 glDrawArrays()。
一些源代码:
渲染:
public static void render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for(VBO vbo : vbos)
{
//disable texture smoothing
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, vbo.texture);
glBindBuffer(GL_ARRAY_BUFFER, vbo.vertex_handle);
glVertexPointer(vbo.vertex_size, GL_FLOAT, 0, 0l);
glBindBuffer(GL_ARRAY_BUFFER, vbo.texture_handle);
glTexCoordPointer(vbo.texture_size, GL_FLOAT, 0, 0l);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, vbo.vertices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
block 加载检测:
int chunkX = (int)(camera.getX() * (-1) / GameData.CHUNK_SIZE);
int chunkZ = (int)(camera.getZ() * (-1) / GameData.CHUNK_SIZE);
for(int cx = 0;cx < GameData.NUMBER_OF_CHUNKS;cx++)
{
for(int cz = 0;cz < GameData.NUMBER_OF_CHUNKS;cz++)
{
boolean shouldBeLoaded = (Math.abs(chunkX - cx) <= 2 && Math.abs(chunkZ - cz) <= 2);
if(GameData.chunks[cx][cz] != shouldBeLoaded)
{
if(shouldBeLoaded)
Util.loadChunk(cx, cz);//loads every block in the chunk
else
Util.unloadChunk(cx, cz);
}
}
}
block 加载:
public void load()
{
try{
if(GameData.map[x][y+1][z] == null || GameData.map[x][y+1][z].id == 2)
top = Util.createQuad(x, y+1, z, 1, id);
}catch(IndexOutOfBoundsException e){};
try{
if(GameData.map[x][y-1][z] == null || GameData.map[x][y-1][z].id == 2)
bot = Util.createQuad(x, y, z, 1, id);
}catch(IndexOutOfBoundsException e){};
try{
if(GameData.map[x-1][y][z] == null || GameData.map[x-1][y][z].id == 2)
left = Util.createQuad(x, y, z, 0, id);
}catch(IndexOutOfBoundsException e){};
try{
if(GameData.map[x+1][y][z] == null || GameData.map[x+1][y][z].id == 2)
right = Util.createQuad(x+1, y, z, 0, id);
}catch(IndexOutOfBoundsException e){};
try{
if(GameData.map[x][y][z-1] == null || GameData.map[x][y][z-1].id == 2)
front = Util.createQuad(x, y, z, 2, id);
}catch(IndexOutOfBoundsException e){};
try{
if(GameData.map[x][y][z+1] == null || GameData.map[x][y][z+1].id == 2)
back = Util.createQuad(x, y, z+1, 2, id);
}catch(IndexOutOfBoundsException e){};
}
Util.createQuad()
public static VBO createQuad(float x, float y, float z, int axis, int id)
{
boolean xA = axis == 0;
boolean yA = axis == 1;//senkrecht
boolean zA = axis == 2;
FloatBuffer vertex_data = BufferUtils.createFloatBuffer(6 * 3);
if(xA)
{
vertex_data.put(new float[] { x, y, z, });
vertex_data.put(new float[] { x, y, z+1, });
vertex_data.put(new float[] { x, y+1, z+1, });
vertex_data.put(new float[] { x, y, z, });
vertex_data.put(new float[] { x, y+1, z, });
vertex_data.put(new float[] { x, y+1, z+1, });
}
if(yA)
{
vertex_data.put(new float[] { x, y, z, });
vertex_data.put(new float[] { x, y, z+1, });
vertex_data.put(new float[] { x+1, y, z+1, });
vertex_data.put(new float[] { x, y, z, });
vertex_data.put(new float[] { x+1, y, z, });
vertex_data.put(new float[] { x+1, y, z+1, });
}
if(zA)
{
vertex_data.put(new float[] { x, y, z, });
vertex_data.put(new float[] { x+1, y, z, });
vertex_data.put(new float[] { x+1, y+1, z, });
vertex_data.put(new float[] { x, y, z, });
vertex_data.put(new float[] { x, y+1, z, });
vertex_data.put(new float[] { x+1, y+1, z, });
}
FloatBuffer texture_data = BufferUtils.createFloatBuffer(6 * 2);
texture_data.put(new float[] { 0f, 0f, });
texture_data.put(new float[] { 1f, 0f, });
texture_data.put(new float[] { 1f, 1f, });
texture_data.put(new float[] { 0f, 0f, });
texture_data.put(new float[] { 0f, 1f, });
texture_data.put(new float[] { 1f, 1f, });
vertex_data.flip();
texture_data.flip();
int vbo_vertex_handle = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo_vertex_handle);
glBufferData(GL_ARRAY_BUFFER, vertex_data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
int vbo_texture_handle = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo_texture_handle);
glBufferData(GL_ARRAY_BUFFER, texture_data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
VBO vbo = new VBO(vbo_vertex_handle, vbo_texture_handle, renderEngine.textures.get(id), 6, 3, 2);
if(id == 2)//transparent
renderEngine.vbos.add(vbo);
else
renderEngine.vbos.add(0, vbo);;
return vbo;
}
最佳答案
我目前正在使用显示列表,因为我对 lwjgl 还很陌生,并且发现 VBO 非常令人困惑,我所做的只是检查玩家是否移动了 16 个方 block 或更多,或者删除/放置了一个方 block ,这就是我更新我的列表,否则我不会更新列表,您还应该停止渲染您看不到的任何面,因此在检查要绘制哪些面时,您还应该检查它们是否不在您的 View 范围内,这是一个示例我愿意;此示例检查我是否应该渲染某个 block 的顶面: if(PlayerY < BlockY) { then return false; },这样您就不需要渲染您可能看不到的东西。
使用显示列表,在 48*48 的 View 范围内,我可以获得大约 200 - 300 FPS,在 128 时,您将获得大约 20 - 100 FPS。 编辑: 如果您还没有开始使用纹理图集,您也应该开始使用,这会减少 bind() 调用,从而使其速度更快。
祝你好运,希望我能有所帮助:)
关于java - LWJGL 加速渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25435823/