Search code examples
opengltextures

OpenGL textures are being rendered with weird interruptions


I am working on an OpenGL engine and my textures are being rendered weirdly. The textures are mostly full and working, but they have little weird interruptions. Here's what it looks like.

Image

The bottom right corner is what the textures are supposed to look like, there are also randomly colored squares of blue peppered in there. These solid squares (not textured) do not have these interruptions.

I can provide code, but I'm not sure what to show because I've checked everywhere and I don't know where the problem is from.

I am working on a Java and a C++ version. Here is the renderer in Java (If you want to see something else just ask):

public class BatchRenderer2D extends Renderer2D {

    private static final int MAX_SPRITES = 60000;
    private static final int VERTEX_SIZE = Float.BYTES * 3 + + Float.BYTES * 2 + Float.BYTES * 1 + Float.BYTES * 1;
    private static final int SPRITE_SIZE = VERTEX_SIZE * 4;
    private static final int BUFFER_SIZE = SPRITE_SIZE * MAX_SPRITES;
    private static final int INDICES_SIZE = MAX_SPRITES * 6;

    private static final int SHADER_VERTEX_INDEX = 0;
    private static final int SHADER_UV_INDEX = 1;
    private static final int SHADER_TID_INDEX = 2;
    private static final int SHADER_COLOR_INDEX = 3;

    private int VAO;
    private int VBO;
    private IndexBuffer IBO;
    private int indexCount;
    private FloatBuffer buffer;

    private List<Integer> textureSlots = new ArrayList<Integer>();

    public BatchRenderer2D() {
        init();
    }

    public void destroy() {
        IBO.delete();
        glDeleteBuffers(VBO);

        glDeleteVertexArrays(VAO);
        glDeleteBuffers(VBO);
    }

    public void init() {
        VAO = glGenVertexArrays();
        VBO = glGenBuffers();

        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, BUFFER_SIZE, GL_DYNAMIC_DRAW);

        glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
        glEnableVertexAttribArray(SHADER_UV_INDEX);
        glEnableVertexAttribArray(SHADER_TID_INDEX);
        glEnableVertexAttribArray(SHADER_COLOR_INDEX);

        glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, false, VERTEX_SIZE, 0);
        glVertexAttribPointer(SHADER_UV_INDEX, 2, GL_FLOAT, false, VERTEX_SIZE, 3 * 4);
        glVertexAttribPointer(SHADER_TID_INDEX, 1, GL_FLOAT, false, VERTEX_SIZE, 3 * 4 + 2 * 4);
        glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_UNSIGNED_BYTE, true, VERTEX_SIZE, 3 * 4 + 2 * 4 + 1 * 4);

        glBindBuffer(GL_ARRAY_BUFFER, 0);

        int[] indices = new int[INDICES_SIZE];

        int offset = 0;
        for (int i = 0; i < INDICES_SIZE; i += 6) {
            indices[  i  ] = offset + 0;
            indices[i + 1] = offset + 1;
            indices[i + 2] = offset + 2;

            indices[i + 3] = offset + 2;
            indices[i + 4] = offset + 3;
            indices[i + 5] = offset + 0;

            offset += 4;
        }

        IBO = new IndexBuffer(indices, INDICES_SIZE);

        glBindVertexArray(0);
    }

    @Override
    public void begin() {
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        buffer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY).asFloatBuffer();
    }

    @Override
    public void submit(Renderable2D renderable) {
        Vector3f position = renderable.getPosition();
        Vector2f size = renderable.getSize();
        Vector4f color = renderable.getColor();
        List<Vector2f> uv = renderable.getUV();
        float tid = renderable.getTID();

        float c = 0;

        float ts = 0.0f;
        if (tid > 0) {
            boolean found = false;
            for(int i = 0; i < textureSlots.size(); i++) { 
                if(textureSlots.get(i) == tid) {
                    ts = (float)(i + 1);
                    found = true;
                    break;
                }
            }

            if(!found) {
                if(textureSlots.size() >= 32) {
                    end();
                    flush();
                    begin();
                }
                textureSlots.add((int)tid);
                ts = (float)textureSlots.size();
            }
        } else {
            int r = (int) (color.x * 255);
            int g = (int) (color.y * 255);
            int b = (int) (color.z * 255);
            int a = (int) (color.w * 255);
            c = Float.intBitsToFloat((r << 0) | (g << 8) | (b << 16) | (a << 24));
        }

        transformationBack.multiply(position).store(buffer);
        uv.get(0).store(buffer);
        buffer.put(ts);
        buffer.put(c);

        transformationBack.multiply(new Vector3f(position.x, position.y + size.y, position.z)).store(buffer);
        uv.get(1).store(buffer);
        buffer.put(ts);
        buffer.put(c);

        transformationBack.multiply(new Vector3f(position.x + size.x, position.y + size.y, position.z)).store(buffer);
        uv.get(2).store(buffer);
        buffer.put(ts);
        buffer.put(c);

        transformationBack.multiply(new Vector3f(position.x + size.x, position.y, position.z)).store(buffer);
        uv.get(3).store(buffer);
        buffer.put(ts);
        buffer.put(c);

        indexCount += 6;
    }

    @Override
    public void end() {
        glUnmapBuffer(GL_ARRAY_BUFFER);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    @Override
    public void flush() {
        for(int i = 0; i < textureSlots.size(); i++) {
            glActiveTexture(GL_TEXTURE0 + i);
            glBindTexture(GL_TEXTURE_2D, textureSlots.get(i));
        }

        glBindVertexArray(VAO);
        IBO.bind();

        glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, NULL);

        IBO.unbind();
        glBindVertexArray(0);

        indexCount = 0;
    }

}

Solution

  • You didn't provide but I'm pretty sure I know the reason (had same problem, following The Cherno tutorial? ;)). Just as information, what is your gpu? (It seems AMD has more problems). Linking my thread for source

    Important part:

    Fragment Shader:

    #version 330 core
    
    if(fs_in.tid > 0.0){    
        int tid = int(fs_in.tid - 0.5);
        texColor = texture(textures[tid], fs_in.uv);
    }
    

    What you try to do here is not allowed as per the GLSL 3.30 specification which states

    Samplers aggregated into arrays within a shader (using square brackets [ ]) can only be indexed with integral constant expressions (see section 4.3.3 “Constant Expressions”). Your tid is not a constant, so this will not work.

    In GL 4, this constraint has been somewhat relaxed to (quote is from GLSL 4.50 spec):

    When aggregated into arrays within a shader, samplers can only be indexed with a dynamically uniform integral expression, otherwise results are undefined. Your now your input also isn't dynamically uniform either, so you will get undefined results too.

    (Thanks derhass)

    One "simple" solution(but not pretty and I believe with a small impact on performance):

    switch(tid){
        case 0: textureColor = texture(textures[0], fs_in.uv); break;
        ...
        case 31: textureColor = texture(textures[31], fs_in.uv); break;
    }
    

    Also, as a small note, you're doing a lot of matrix multiplication there for squares, you could simply multiply the first one and then go and add the values, it boosted my performance around 200 fps's (in your example, multiply, then add y, then add x, then subtract y again)

    Edit:

    Clearly my algebra is not where it should be, what I said you could do(and is now with strike) is completely wrong, sorry