Austin Romney
Austin Romney

Reputation: 1

Only one texel rendering during mass batch rendering using SSBOs

First, this is my second time asking this question. The first time, I was given many different tips that I applied into this new question. Currently, I am working upon a 2D tile renderer. Switching VAOs i found to be too performance draining. Therefore, I decided upon batching the tiles. I have been able to render each tile in a defined grid size. I decided that I would use a single texture atlas, and in order to set each texture, I would just change their texture coordinates. I decided that I would do this with an SSBO, and that within the shader, I would use gl_InstanceId to find which set of vertices or texture coordinates to use. For now, I have just applied the same texture coordinates to all of them, and later I will change this.

The problem, however, is that rather than the texture being applied to each tile, only one texel is being rendered, resulting in one, solid color.

Here is the result Non-wireframe render

Here is the wireframe variant wireframe render

finally, here is my minimal reproducible example; (note, the only external code is for the texture class. I had tested this class before, and it had worked. I cut it because i found the code too long. If you want me to add it, please let me know.) I know it is long, but this is all of the code involved, and I have been able to track the problem down (Maybe?) I think it might be something to do with the texture coordinates. Although, I am not sure how to fix this, or even what the problem there is.

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

here is my shader code;

fragment shader

#version 460

out vec4 fragColor;

uniform sampler2D texture_sampler;

in vec2 outTP;

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

vertex shader

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

Edit; I found some more potentially helpful information. I found one error with my offset calculations. Now my tiles are in the right position, my offset values were half what they should have been. I also decided to debug, and I replaced the individualized texture coordinate system with every tile using one texture coordinate. with this, I was able to get them to have an image. Although, I still need to get the individualized system to work. This does, however, prove that the problem is within the texture coordinate calculations.

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

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

Now this above code should be the problem. How do I batch together texture coordinates?

Upvotes: -1

Views: 48

Answers (1)

Austin Romney
Austin Romney

Reputation: 1

I found the answer! I realized that instead of passing all texture coordinate data, I could just add offsets and reuse texture coordinates!

Here is how I rewrote my shader code. I simply passed the texture coordinates as I did with the vertices!

#version 460

layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texPos;

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

layout(std430, binding = 1) buffer TextureOffsets {
    vec2 texOffset[];
};

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 = texPos+texOffset[index].xy;

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

Upvotes: 0

Related Questions