【问题标题】:Fastest way to render 2D tiles using LWJGL?使用 LWJGL 渲染 2D 瓷砖的最快方法?
【发布时间】:2017-06-27 13:26:42
【问题描述】:

我开始观看 these 使用 LWJGL 创建 2d 自上而下游戏的教程,我读到 VBO 应该很快,但对于每帧渲染 48*48 瓦片,我只能得到大约 100FPS,这很慢,因为我会添加游戏中包含更多内容,而不仅仅是一些静态的、不动的或变化的瓷砖。

我可以做些什么来加快速度?请记住,我刚开始学习 lwjgl 和 opengl,所以我可能不会知道很多东西。

无论如何,这是我的代码的一些部分(我从代​​码中删除了一些有点无意义的部分并用一些描述替换它们):

主循环

double targetFPS = 240.0;
        double targetUPS = 60.0;

        long initialTime = System.nanoTime();
        final double timeU = 1000000000 / targetUPS;
        final double timeF = 1000000000 / targetFPS;
        double deltaU = 0, deltaF = 0;
        int frames = 0, updates = 0;
        long timer = System.currentTimeMillis();

        while (!window.shouldClose()) {
            long currentTime = System.nanoTime();
            deltaU += (currentTime - initialTime) / timeU;
            deltaF += (currentTime - initialTime) / timeF;
            initialTime = currentTime;

            if (deltaU >= 1) {
                // --- [ update ] ---
                --INPUT HANDLING FOR BASIC MOVEMENT, CLOSING THE GAME AND TURNING VSYNC ON AND OFF USING A METHOD FROM THE INPUT HANDLER CLASS--

                world.correctCamera(camera, window);

                window.update();

                updates++;
                deltaU--;
            }

            if (deltaF >= 1) {
                // --- [ render ] ---
                glClear(GL_COLOR_BUFFER_BIT);
                world.render(tileRenderer, shader, camera, window);
                window.swapBuffers();

                frames++;
                deltaF--;
            }
            --PRINTING THE FPS AND UPS EVERY SECOND--
        }

使用的输入处理方法:

I have this in my constructor:
this.keys = new boolean[GLFW_KEY_LAST];
for(int i = 0; i < GLFW_KEY_LAST; i++)
    keys[i] = false;

And here are the methods: 
public boolean isKeyDown(int key) {
    return glfwGetKey(window, key) == 1;
}
public boolean isKeyPressed(int key) {
    return (isKeyDown(key) && !keys[key]);
}
public void update() {
    for(int i = 32; i < GLFW_KEY_LAST; i++)
        keys[i] = isKeyDown(i);
}

这是 World 类的渲染方法:

public void render(TileRenderer renderer, Shader shader, Camera camera, Window window) {
    int posX = ((int) camera.getPosition().x + (window.getWidth() / 2)) / (scale * 2);
    int posY = ((int) camera.getPosition().y - (window.getHeight() / 2)) / (scale * 2);
    for (int i = 0; i < view; i++) {
        for (int j = 0; j < view; j++) {
            Tile t = getTile(i - posX, j + posY);
            if (t != null)
                renderer.renderTile(t, i - posX, -j - posY, shader, world, camera);
        }
    }
}

这是 TileRenderer 的 renderTile() 方法:

public void renderTile(Tile tile, int x, int y, Shader shader, Matrix4f world, Camera camera) {
    shader.bind();
    if (tileTextures.containsKey(tile.getTexture()))
        tileTextures.get(tile.getTexture()).bind(0);

    Matrix4f tilePosition = new Matrix4f().translate(new Vector3f(x * 2, y * 2, 0));
    Matrix4f target = new Matrix4f();

    camera.getProjection().mul(world, target);
    target.mul(tilePosition);

    shader.setUniform("sampler", 0);
    shader.setUniform("projection", target);

    model.render();
}

这是 Model 类的构造函数和渲染方法:

public Model(float[] vertices, float[] texture_coords, int[] indices) {
    draw_count = indices.length;

    v_id = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, v_id);
    glBufferData(GL_ARRAY_BUFFER, createBuffer(vertices), GL_STATIC_DRAW);

    t_id = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, t_id);
    glBufferData(GL_ARRAY_BUFFER, createBuffer(texture_coords), GL_STATIC_DRAW);

    i_id = glGenBuffers();
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);

    IntBuffer buffer = BufferUtils.createIntBuffer(indices.length);
    buffer.put(indices);
    buffer.flip();

    glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

public void render() {
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, v_id);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, t_id);
    glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);
    glDrawElements(GL_TRIANGLES, draw_count, GL_UNSIGNED_INT, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
}

我将顶点、纹理坐标和索引存储在图块渲染器中:

float[] vertices = new float[]{
            -1f, 1f, 0, //top left     0
            1f, 1f, 0, //top right     1
            1f, -1f, 0, //bottom right 2
            -1f, -1f, 0, //bottom left 3
    };

    float[] texture = new float[]{
            0, 0,
            1, 0,
            1, 1,
            0, 1,
    };

    int[] indices = new int[]{
            0, 1, 2,
            2, 3, 0
    };

我不知道还能放什么,但完整的源代码和资源 + 着色器文件可在 github here 上找到。

【问题讨论】:

  • 将所有静态图块捆绑到一个模型中,以减少绘制调用 (glDrawElements() & co.) 和相关状态更改的次数。还可以查看纹理图集和/或数组纹理。
  • 我该怎么做? (捆绑瓷砖)
  • 在你的vertices/texture/indices`数组中没有一个四边形的几何价值,你有整个层的价值。
  • 但是,我该怎么做呢?我应该在谷歌上搜索答案/教程吗?就像我说的,我是一个完全的菜鸟,我并没有完全理解我看过的教程中的所有内容

标签: java opengl 2d lwjgl


【解决方案1】:

对于您当前的系统,我建议您根据纹理对图块进行分组。创建类似这样的内容:

Map&lt;Texture, List&lt;Tile&gt;&gt; tiles = new HashMap&lt;Texture, List&lt;Tile&gt;&gt;()

然后,当您渲染图块地图时,您只需为每组图块设置一次纹理,而不是每个图块一次。这节省了用于将纹理/纹理 ID 推送到 GPU 的 PCI-E 带宽。你可以这样实现(伪代码):

for (Texture tex : tile.keySet())
{
    BIND TEXTURE
    for (Tile tile : tiles.get(tex))
    {
        SET UNIFORMS
        RENDER
    }
}

我在这些方面看到的其他一些情况是,您正在将投影矩阵分别推送到每个图块。当您运行着色器程序时,给定统一体的值保持不变,直到您更改它或直到程序结束。设置一次投影矩阵均匀。

您似乎也在每个renderTile(...) 都调用它。给定值不变,在渲染传递之前计算一次,然后在renderTile(...)方法中将其作为变量传入,而不是传入cameraworld

【讨论】:

  • 那么我可以去掉 shader.setUniform() 行,只在设置投影后开始游戏时调用一次吗?如果我这样做,opengl 将如何知道将瓷砖放在哪里,因为在第二个 setUniform 中我也传递了包含位置的目标矩阵?我想我误解了你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-28
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-27
相关资源
最近更新 更多