使用 SSBO 进行批量渲染时仅渲染一个纹理元素

问题描述 投票:0回答:1

首先,这是我第二次问这个问题。第一次,我得到了许多不同的技巧,我将它们应用于这个新问题。 目前,我正在开发 2D 图块渲染器。我发现切换 VAO 太消耗性能了。因此,我决定对瓷砖进行批处理。我已经能够以定义的网格大小渲染每个图块。我决定使用单个纹理图集,为了设置每个纹理,我只需更改它们的纹理坐标。我决定使用 SSBO 来完成此操作,并且在着色器中,我将使用 gl_InstanceId 来查找要使用的顶点或纹理坐标集。目前,我刚刚对所有这些应用了相同的纹理坐标,稍后我将更改它。

然而,问题在于,不是将纹理应用于每个图块,而是仅渲染一个纹理像素,从而产生一种纯色。

这是结果 非线框渲染

这是线框变体 线框渲染

最后,这是我的最小可重现示例; (注意,唯一的外部代码是纹理类。我之前测试过这个类,它有效。我把它剪掉了,因为我发现代码太长了。如果你想让我添加它,请告诉我。) 我知道它很长,但这就是所涉及的所有代码,并且我已经能够找到问题所在(也许?) 我认为这可能与纹理坐标有关。虽然,我不确定如何解决这个问题,甚至不知道问题是什么。

package test;

import gfx.Render;
import gfx.ShaderProgram;
import gfx.Texture;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryUtil;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL31.glDrawElementsInstanced;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.system.MemoryUtil.memFree;

public class MinimalReproducibleExample {
    private int mapWidth = 50, mapHeight = 50;
    private float tileSize = 0.1f;
    private int vaoId;
    private int iboId;
    public float camX = 0.0f, camY = 0.0f;
    public float scale = 1.0f;

    private int usboId;
    Texture tt;

    ShaderProgram shaderProgram;

    private float[] vertices;
    private final int[] indices = new int[]{
            0, 1, 3, 3, 1, 2,
    };

    private FloatBuffer offsetBuffer;
    private FloatBuffer tpBuffer;
    long windowId;
    private Matrix4f projectionMatrix;

    static float safeZoneX, safeZoneY;

    private void setupOrthographicProjection() {
        float orthoHeight = 10.0f; // Adjust this based on your scene's needs
        float orthoWidth = orthoHeight * 1080/920;
        safeZoneX = orthoWidth;
        safeZoneY = orthoHeight;
        projectionMatrix = new Matrix4f().setOrtho2D(-orthoWidth / 2, orthoWidth / 2, -orthoHeight / 2, orthoHeight / 2);
    }

    public MinimalReproducibleExample() {
        if(!glfwInit())
            System.exit(1);
        windowId = glfwCreateWindow(1080, 920, "TEST WINDOW", 0, 0);
        if(windowId == 0)
            System.exit(1);

        glfwMakeContextCurrent(windowId);

        glfwShowWindow(windowId);


        GL.createCapabilities();

        setupOrthographicProjection();
        ShaderProgram.loadShaderPrograms();

        tt = Texture.loadTexture("texture_atlases/atlas-1.png");

        float[] offsets = new float[mapWidth * mapHeight * 2];
        float[] texPos = new float[mapWidth * mapHeight * 8];

        vertices = new float[]{
                -tileSize, tileSize, 0.0f,
                -tileSize, -tileSize, 0.0f,
                tileSize, -tileSize, 0.0f,
                tileSize, tileSize, 0.0f,
        };

        float[] test = new float[]{
                0.0f, 1.0f,
                0.0f, 0.0f,
                1.0f, 0.0f,
                1.0f, 1.0f
        };

        int index = 0;
        for (int x = 0; x < mapWidth; x++) {
            for (int y = 0; y < mapHeight; y++) {
                float xPos = (float) x * tileSize;
                float yPos = (float) y * tileSize;
                offsets[index++] = xPos;
                offsets[index++] = yPos;
            }
        }
        index = 0;
        for(int i = 0; i < mapWidth * mapHeight * 8; i++) {
            texPos[i] = test[index];

            index++;
            if(index >= 8) {
                index = 0;
            }
        }

        offsetBuffer = MemoryUtil.memAllocFloat(offsets.length);
        offsetBuffer.put(offsets).flip();

        tpBuffer = MemoryUtil.memAllocFloat(texPos.length);
        tpBuffer.put(texPos).flip();

        shaderProgram = ShaderProgram.getShaderProgram("test_program");
        ShaderProgram.useProgram("test_program");

        vaoId = glGenVertexArrays();
        glBindVertexArray(vaoId);

        // Store vertex data off-heap
        FloatBuffer vertexData = MemoryUtil.memAllocFloat(vertices.length);
        vertexData.put(vertices).flip();

        // Create and bind VBO for vertices
        int vboId = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboId);
        glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        glEnableVertexAttribArray(0);
        memFree(vertexData);

        // Store index data off-heap
        IntBuffer indexData = MemoryUtil.memAllocInt(indices.length);
        indexData.put(indices).flip();

        // Create and bind IBO for indices
        iboId = glGenBuffers();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData, GL_STATIC_DRAW);
        memFree(indexData);

        // Create and bind the SSBO for offsets
        int ssboId = glGenBuffers();
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboId);
        glBufferData(GL_SHADER_STORAGE_BUFFER, offsetBuffer, GL_STATIC_DRAW);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssboId);

        // Create and bind the SSBO for texture coordinates
        usboId = glGenBuffers();
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, usboId);
        glBufferData(GL_SHADER_STORAGE_BUFFER, tpBuffer, GL_STATIC_DRAW);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, usboId);

        // Unbind VAO
        glBindVertexArray(0);

        glfwSetKeyCallback(windowId, new GLFWKeyCallback() {
            @Override
            public void invoke(long l, int i, int i1, int i2, int i3) {
                if(i == GLFW_KEY_F1) {
                    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                    glDisable(GL_TEXTURE_2D);
                }
                if(i == GLFW_KEY_F2) {
                    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                    glEnable(GL_TEXTURE_2D);
                }
            }
        });



        while(!glfwWindowShouldClose(windowId)) {
            glfwPollEvents();

            //handle camera movement;
            if(glfwGetKey(windowId, GLFW_KEY_D) == GLFW_TRUE) {
                camX -= 0.01f;
            }

            if(glfwGetKey(windowId, GLFW_KEY_A) == GLFW_TRUE) {
                camX += 0.01f;
            }
            if(glfwGetKey(windowId, GLFW_KEY_W) == GLFW_TRUE) {
                camY -= 0.01f;
            }

            if(glfwGetKey(windowId, GLFW_KEY_S) == GLFW_TRUE) {
                camY += 0.01f;
            }
            if(glfwGetKey(windowId, GLFW_KEY_E) == GLFW_TRUE) {
                scale += 0.001f;
            }
            if(glfwGetKey(windowId, GLFW_KEY_Q) == GLFW_TRUE) {
                scale -= 0.001f;
            }
            if(scale >=10.0f) {
                scale = 10.0f;
            }
            if(scale<=0.01f) {
                scale = 0.01f;
            }

            render();
        }
        glfwDestroyWindow(windowId);
        glfwTerminate();

    }

    float tick = 0;
    public void render() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        tick += 0.01f;
        ShaderProgram.useProgram("test_program");
        glBindVertexArray(vaoId);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId);
        shaderProgram.setUniform("delta", tick);
        shaderProgram.setUniform("projectionMatrix", projectionMatrix);
        shaderProgram.setUniform("texture_sampler", 0); // Ensure correct texture unit
        shaderProgram.setUniform("scale", scale);
        shaderProgram.setUniform("camPos", camX, camY);

        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, tt.getId());

        glDrawElementsInstanced(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0, mapWidth * mapHeight);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
        ShaderProgram.unbindProgram();


        glfwSwapBuffers(windowId);
    }



    public static void main(String[] args) {
        new MinimalReproducibleExample();
    }
}

这是我的着色器代码;

片段着色器

#version 460

out vec4 fragColor;

uniform sampler2D texture_sampler;

in vec2 outTP;

void main() {
    fragColor = texture(texture_sampler, outTP);
}

顶点着色器

#version 460

layout(location = 0) in vec3 position;

layout(std430, binding = 0) buffer OffsetPositions {
    vec2 offsets[];
};

layout(std430, binding = 1) buffer TexturePositions {
    vec2 textCoords[];
};

uniform float delta;
uniform mat4 projectionMatrix;
uniform vec2 camPos;
uniform float scale;

out vec2 outTP;

void main() {
    int index = gl_InstanceID;
    vec2 offset = offsets[index];
    outTP = textCoords[index];

    gl_Position = projectionMatrix * vec4(
        position.xy * scale + offset * scale + camPos * scale,
        position.z,
        1.0
    );
}

编辑; 我发现了一些更多可能有用的信息。 我发现偏移量计算有一个错误。现在我的图块位于正确的位置,我的偏移值是应有的一半。 我还决定进行调试,并用每个图块使用一个纹理坐标替换了个性化纹理坐标系。有了这个,我就能让他们有一个形象。尽管如此,我仍然需要让个性化系统发挥作用。然而,这确实证明问题出在纹理坐标计算中。

index = 0;
        for(int i = 0; i < mapWidth * mapHeight * 8; i++) {
            texPos[i] = test[index];

            index++;
            if(index >= 8) {
                index = 0;
            }
        }

现在上面的代码应该是问题所在。如何批量处理纹理坐标?

java opengl graphics game-development lwjgl
1个回答
0
投票

我找到答案了!我意识到,我可以只添加偏移量并重用纹理坐标,而不是传递所有纹理坐标数据!

© www.soinside.com 2019 - 2024. All rights reserved.