使用LWJGL渲染2D图块的最快方法?

时间:2017-06-27 13:26:43

标签: java opengl 2d lwjgl

我开始观看使用LWJGL创建2D自上而下游戏的these教程,我读到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类的构造函数和render方法:

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);
}

我在tile渲染器中存储顶点,纹理坐标和索引:

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上提供了完整的源代码和资源+着色器文件。

1 个答案:

答案 0 :(得分:0)

使用您当前的系统,我建议您根据纹理对切片进行分组。创建这样的东西:

Map<Texture, List<Tile>> tiles = new HashMap<Texture, List<Tile>>()

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

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

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

您似乎每隔renderTile(...)调用一次。如果值未更改,请在渲染过程之前计算一次,然后将其作为renderTile(...)方法中的变量传递,而不是传入cameraworld