【LWJGL官方教程】批渲染

本文档主要介绍了LWJGL中批处理渲染的原理和使用方法,包括批次初始化、用批次描画以及缓冲映射。通过批处理可以优化动态场景的渲染效率,减少状态变化,并在上传到GPU之前收集数据。详细内容涵盖了如何创建和管理VAO、VBO、FloatBuffer,以及如何使用顶点和片段着色器。此外,还探讨了不同的缓冲访问提示和缓冲映射技术,以提高大数据量渲染时的性能。
摘要由CSDN通过智能技术生成

原文:https://github.com/SilverTiger/lwjgl3-tutorial/wiki/Batching
译注:并没有逐字逐句翻译一切,只翻译了自己觉得有用的部分。另外此翻译仅供参考,请一切以原文为准。代码例子文件链接什么的都请去原链接查找。括号里的内容一般也是译注,供理解参考用。总目录传送门:http://blog.csdn.net/zoharwolf/article/details/49472857

最近几章好像都是静态场景,在真的游戏里,这可不行。我们需要动态场景。用批渲染可以减少状态变化,还能在上传GPU之前收集数据。

Initializing a batch 批次初始化

初始化前,我们先看看都需要啥。
目前清楚的,我们需要VAO、VBO、顶点shader、片段shader、shader程序。批渲染还需要一个FloatBuffer,顶点数,还有一个用来判别是否要立刻开画的布尔值。

private FloatBuffer vertices;
private int numVertices;
private boolean drawing;

现在我们可以像之前一样初始化shader程序。首先我们创建绑定VAO,然后创建VBO,但这次略有不同。创建VBO并绑定后,再创建FloatBuffer,它的尺寸是批次尺寸最大值。批次的最佳尺寸在1~4MB之间,但此教程里我们用16KB的,也就是4096个浮点数。

/* Generate Vertex Buffer Object */
vbo = new VertexBufferObject();
vbo.bind(GL_ARRAY_BUFFER);

/* Create FloatBuffer */
vertices = BufferUtils.createFloatBuffer(4096);

一般我们用顶点数据填充此FloatBuffer,但是既然想动态,那就用glBufferData生成无数据的,但是得告诉OpenGL它的尺寸,从此它就是我们分配VBO的仓库了。另外还要用GL_DYNAMIC_DRAW代替GL_STATIC_DRAW。

/* Upload null data to allocate storage for the VBO */
long size = vertices.capacity() * Float.BYTES;
glBufferData(GL_ARRAY_BUFFER, size, GL_DYNAMIC_DRAW);

现在介绍一下这些不同的缓冲用hint。第一部分,访问频率:

  • STREAM: 数据存储内容只修改一次,只用几次。
  • DYNAMIC: 数据存储内容要反复修改,频繁使用。
  • STATIC: 数据存储内容只修改一次,频繁使用。

第二部分,访问性质:

  • DRAW: 由应用程序修改存储内容,数据用于GL描画和图片规格指令。
  • READ: 通过从GL读取数据修改存储内容,数据用来当应用程序查询时返回数据。
  • COPY: 通过从GL读取数据修改存储内容,数据用于GL描画和图片规格指令。
    现在我们就设numVertices = 0并且 drawing = false,然后初始化shader和shader程序。

Drawing with a batch 用一个批次描画

文本渲染教程里,我们已经见识过了。一个批次从begin()开始,到end()结束。
所以描画代码应该像下面这样:

texture.bind();
batch.begin();
drawObjects();
batch.end();

绑定纹理你应该明白,begin()的内容也很直观:

public void begin() {
    numVertices = 0;
    drawing = true;
}

结束时,清空GPU数据并结束描画。

public void end() {
    flush();
    drawing = false;
}

再看下flush()方法,首先看看臬用数据填充缓冲。你可能还记得文本渲染教程里的drawTextureRegion(texture, drawX, drawY, g.x, g.y, g.width, g.height, c),但这次我们好好看看这方法到底做了什么。

public void drawTextureRegion(Texture texture, float x, float y, float regX, float regY, float regWidth, float regHeight, Color c) {
    float x1 = x;
    float y1 = y;
    float x2 = x + regWidth;
    float y2 = y + regHeight;

    /* Calculate Texture coordinates */
    float s1 = regX / texture.getWidth();
    float t1 = regY / texture.getHeight();
    float s2 = (regX + regWidth) / texture.getWidth();
    float t2 = (regY + regHeight) / texture.getHeight();

    /* Calculate color */
    float r = c.getRed() / 255f;
    float g = c.getGreen() / 255f;
    float b = c.getBlue() / 255f;

    /* Put data into buffer */
    vertices.put(x1).put(y1).put(r).put(g).put(b).put(s1).put(t1);
    vertices.put(x1).put(y2).put(r).put(g).put(b).put(s1).put(t2);
    vertices.put(x2).put(y2).put(r).put(g).put(b).put(s2).put(t2);

    vertices.put(x1).put(y1).put(r).put(g).put(b).put(s1).put(t1);
    vertices.put(x2).put(y2).put(r).put(g).put(b).put(s2).put(t2);
    vertices.put(x2).put(y1).put(r).put(g).put(b).put(s2).put(t1);

    /* We drawed 6 vertices */
    numVertices += 6;
}

这代码不言而喻,每次描画后增加numVertices很重要,当清除的时候我们需要这值。
清除时我们应该先检查缓冲里是否有顶点,空画东西毫无意义。
当知道缓冲里有顶点,我们flip(),然后绑定VAO(如果用OpenGL2.1,我们绑定VBO并指定顶点属性)并且开始使用shader程序。

vertices.flip();
vao.bind();
program.use();

现在该上传数据到GPU了,一般用glBufferData,但是即将已经分配过GPU内存了,我们用glBufferSubData来上传。

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices);

这将会把批次数据放在GPU上,之后用glDrawArrays来画。

glDrawArrays(GL_TRIANGLES, 0, numVertices);

清除的最后一步,清空缓冲并且将顶点数设为0.

vertices.clear();
numVertices = 0;

Buffer mapping 缓冲映射

小数据块可以用glBufferSubData,但是当上传超过1MB的数据时,应该用另一个方法。使用缓冲映射可以提升性能,为此得重写一下begin()和end()。还要用ByteBuffer来代替FloatBuffer。
当然如果你非要用FloatBuffer的话,你也可能用asFloatBuffer()

public void begin() {
    vbo.bind(GL_ARRAY_BUFFER);
    vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY, vertices.capacity(), vertices);
    numVertices = 0;
    drawing = true;
}

public void end() {
    glUnmapBuffer(GL_ARRAY_BUFFER);

    flush();
    drawing = false;
}

调用glMapBuffer 将会得到一个指定缓冲数据仓库的指针(应该是句柄才对),之后你可以直接往里写。当然除了GL_WRITE_ONLY,还可以用的是GL_READ_ONLY和GL_READ_WRITE。
用缓冲之前,应该用glUnmapBuffer取消映射。之后就不用再上传数据到GPU,数据已经在里面了,你可以直接画。

下一篇学习怎样处理输入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值