OpenGL Quads Rendering in Black When Rendering Text

0

So, I'm using LWJGL and OpenGL to render some quads and textures. My quads have been working fine, until the addition of some textures. Specifically, when I added the ability to render text to the screen, all of the quads color has changed to black. I've looked around for a solution but have had no luck, the most common answers are to use glEnable(GL_TEXTURE_2D); only when I draw textures, otherwise keep it disabled when drawing quads. This doesn't seem to be working for me, my quads are still black. I've included my Renderer, Font, and Texture classes below as I'm fairly certain the issue is somewhere in there.

Edit: So I've done a bit more testing, I wasn't even trying to draw normal textures (from .png files) however, now that I've tried to do that as well, I've found they also are drawn completely black. So as of now, the only thing rendering correctly is my font/text, even though I create a texture to render them as well

Renderer.java

public class Renderer {

    private VertexArrayObject vao;
    private VertexBufferObject vbo;
    private ShaderProgram shaderProgram;

    private FloatBuffer vertices;
    private int numVertices;
    private boolean drawing;

    private Font fontSketchalot;

    public void drawQuad(float x, float y, float width, float height, Colors c) {
        glDisable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, 0);

        begin();

        if (vertices.remaining() < 5 * 7) flush();

        // Calculate Vertex positions
        float x1 = x;
        float y1 = y;
        float x2 = x + width;
        float y2 = y - height;

        // Calculate color
        float r = c.getR();
        float g = c.getG();
        float b = c.getB();

        // Put data into buffer
        vertices.put(x1).put(y1).put(r).put(g).put(b).put(0f).put(1f);
        vertices.put(x1).put(y2).put(r).put(g).put(b).put(0f).put(0f);
        vertices.put(x2).put(y2).put(r).put(g).put(b).put(1f).put(0f);
        vertices.put(x2).put(y1).put(r).put(g).put(b).put(1f).put(1f);

        // We drew X vertices
        numVertices += 4;

        end();
    }

    public void drawTextureFromTexture(Texture texture, float x, float y, float width, float height) {
        drawTexture(texture, x, y, x + width, y - height, 0, 0, 1, 1, Colors.white);
    }

    public void drawTextureFromFont(Texture texture, float x, float y, float width, float height, float s1, float t1, float sWidth, float sHeight, Colors c) {
        drawTexture(texture, x, y, x + width, y - height, s1, t1, s1 + sWidth, t1 + sHeight, c);
    }

    public void drawTexture(Texture texture, float x1, float y1, float x2, float y2, float s1, float t1, float s2, float t2, Colors c) {
        glEnable(GL_TEXTURE_2D);

        texture.prepare();

        begin();

        if (vertices.remaining() < 5 * 7) flush();

        // Calculate color
        float r = c.getR();
        float g = c.getG();
        float b = c.getB();

        // Put data into buffer
        vertices.put(x1).put(y1).put(r).put(g).put(b).put(s1).put(t2);
        vertices.put(x1).put(y2).put(r).put(g).put(b).put(s1).put(t1);
        vertices.put(x2).put(y2).put(r).put(g).put(b).put(s2).put(t1);
        vertices.put(x2).put(y1).put(r).put(g).put(b).put(s2).put(t2);

        // We drew X vertices
        numVertices += 4;

        end();

        texture.unbind();
    }

    public void drawText(String text, float x, float y, float scale, Colors c) {
        fontSketchalot.drawText(this, text, x, y, scale, c);
    }

    // Initialize renderer
    public void init(){

        // Create font
        fontSketchalot = new Font(Fonts.SKETCHALOT);

        // Set up shader programs
        setupShaderProgram();

        // Set wrapping and filtering values
        setParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        setParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        setParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        setParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        // Enable blending (?????)
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    }

    // Set parameter of texture
    private void setParameter(int name, int value) {
        glTexParameteri(GL_TEXTURE_2D, name, value);
    }

    // Clears drawing area
    public void clear() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    }

    // Begin rendering
    public void begin() {
        if (drawing) throw new IllegalStateException("Renderer is already drawing.");
        drawing = true;
        numVertices = 0;
    }

    // End rendering
    public void end() {
        if (!drawing) throw new IllegalStateException("Renderer is not drawing.");
        drawing = false;
        flush();
    }

    // Flushes data to GPU to get rendered
    public void flush() {
        if (numVertices > 0) {
            vertices.flip();

            if (vao != null) vao.bind();
            else vbo.bind(GL_ARRAY_BUFFER);
            specifyVertexAttributes();
        }
        shaderProgram.use();

        // Upload the new vertex data
        vbo.bind(GL_ARRAY_BUFFER);
        vbo.uploadSubData(GL_ARRAY_BUFFER, 0, vertices);

        // Draw batch
        glDrawArrays(GL_QUADS, 0, numVertices);

        // Clear vertex data for next batch
        vertices.clear();
        numVertices = 0;
    }

    private void setupShaderProgram() {

        // Generate VertexArrayObject
        if (Game.is32Supported()) {
            vao = new VertexArrayObject();
            vao.bind();
        } else {
            throw new RuntimeException("OpenGL 3.2 not supported.");
        }

        // Generate VertexBufferObject
        vbo = new VertexBufferObject();
        vbo.bind(GL_ARRAY_BUFFER);



        // Create FloatBuffer
        vertices = MemoryUtil.memAllocFloat(4096);

        // Upload null data to allocate storage for the VBO
        long size = vertices.capacity() * Float.BYTES;
        vbo.uploadData(GL_ARRAY_BUFFER, size, GL_DYNAMIC_DRAW);

        // Initialize variables
        numVertices = 0;
        drawing = false;

        // Load Shaders:
        Shader vertexShader, fragmentShader;
        if (Game.is32Supported()) {
            vertexShader = Shader.loadShader(GL_VERTEX_SHADER, "res/shaders/default.vert");
            fragmentShader = Shader.loadShader(GL_FRAGMENT_SHADER, "res/shaders/default.frag");
        } else {
            throw new RuntimeException("OpenGL 3.2 not supported.");
        }

        // Create ShaderProgram
        shaderProgram = new ShaderProgram();
        shaderProgram.attachShader(vertexShader);
        shaderProgram.attachShader(fragmentShader);
        if (Game.is32Supported()) {
            shaderProgram.bindFragmentDataLocation(0, "fragColor");
        }
        shaderProgram.link();
        shaderProgram.use();

        // Delete linked shaders
        vertexShader.delete();
        fragmentShader.delete();

        // Get width & height of framebuffer
        long window = GLFW.glfwGetCurrentContext();
        int width, height;
        try (MemoryStack stack = MemoryStack.stackPush()) {
            IntBuffer widthBuffer = stack.mallocInt(1);
            IntBuffer heightBuffer = stack.mallocInt(1);
            GLFW.glfwGetFramebufferSize(window, widthBuffer, heightBuffer);
            width = widthBuffer.get();
            height = heightBuffer.get();
        }

        // Specify vertex pointers
        specifyVertexAttributes();

        // Set Model Matrix to identity matrix
        Matrix4f model = new Matrix4f();
        int uniModel = shaderProgram.getUniformLocation("model");
        shaderProgram.setUniform(uniModel, model);

        // Set View Matrix to identity matrix
        Matrix4f view = new Matrix4f();
        int uniView = shaderProgram.getUniformLocation("view");
        shaderProgram.setUniform(uniView, view);

        // Set Projection Matrix to an orthographic projection
        Matrix4f projection = Matrix4f.orthographic(0f, width, 0f, height, -1f, 1f);
        int uniProjection = shaderProgram.getUniformLocation("projection");
        shaderProgram.setUniform(uniProjection, projection);

    }

    // Specifies the vertex shader pointers (attributes)
    private void specifyVertexAttributes() {

        int posAttrib = shaderProgram.getAttributeLocation("position");
        shaderProgram.enableVertexAttribute(posAttrib);
        shaderProgram.pointVertexAttribute(posAttrib, 2, 7 * Float.BYTES, 0);

        int colAttrib = shaderProgram.getAttributeLocation("color");
        shaderProgram.enableVertexAttribute(colAttrib);
        shaderProgram.pointVertexAttribute(colAttrib, 3, 7 * Float.BYTES, 2 * Float.BYTES);

        int texAttrib = shaderProgram.getAttributeLocation("texcoord");
        shaderProgram.enableVertexAttribute(texAttrib);
        shaderProgram.pointVertexAttribute(texAttrib, 2, 7 * Float.BYTES, 5 * Float.BYTES);

        int uniTex = shaderProgram.getUniformLocation("texImage");
        shaderProgram.setUniform(uniTex, 0);

    }

}

Font.java

public class Font {

    private String fontPath;
    private java.awt.Font font;
    private Map<Character, Glyph> glyphs;
    private float fontHeight;
    private Texture texture;

    public Font(String fontPath) {
        System.out.println("Creating font...");
        this.fontPath = fontPath;
        loadFont();
        glyphs = new HashMap<>();
        texture = createFontTexture();
        System.out.println("Font created.");
    }

    public Font() {
        System.out.println("Creating font...");
        this.fontPath = "DEFAULT";
        font = new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 30);
        glyphs = new HashMap<>();
        texture = createFontTexture();
        System.out.println("Font created.");
    }

    private void loadFont() {
        try {
            System.out.println("Loading font...");
            font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, new FileInputStream(fontPath)).deriveFont(java.awt.Font.PLAIN, 30);
            System.out.println("Font loaded.");
        } catch (Exception e) {
            throw new RuntimeException("Could not load font.");
        }
    }

    private Texture createFontTexture() {
        int imageWidth = 0;
        int imageHeight = 0;

        //  Add up total width and height
        for (int i = 32; i < 256; i++) {
            if (i == 127) { // DEL control code
                continue;
            }
            char c = (char) i;
            BufferedImage ch = createCharImage(c);

            if (ch == null) {
                System.out.println("Could not load [CHAR: \"" + c + "\"] from font: " + fontPath);
                continue;
            }

            imageWidth += ch.getWidth();
            imageHeight = Math.max(imageHeight, ch.getHeight());
        }

        fontHeight = Converter.glfwCoordToOpenGLCoord(imageHeight);

        /* Image for the texture */
        BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();


        // Set up glyphs (Map<CHAR, GLYPH>)
        int xOffsetI = 0;
        float xOffsetF = 0;
        for (int i = 32; i < 256; i++) {
            if (i == 127) { // DEL control code
                continue;
            }

            char c = (char) i;
            BufferedImage charImage = createCharImage(c);

            if (charImage == null) {
                System.out.println("Could not load [CHAR: \"" + c + "\"] from font: " + fontPath);
                continue;
            }

            int charWidth = charImage.getWidth();
            int charHeight = charImage.getHeight();

            // Create glyph
            Glyph ch = new Glyph(Converter.glfwCoordToOpenGLCoord(charWidth), Converter.glfwCoordToOpenGLCoord(charHeight), ((float)charWidth / (float)imageWidth), (float)charHeight / (float)imageHeight, xOffsetF, 0.0f);
            // Draw char on image
            g.drawImage(charImage, xOffsetI, 0, null);
            xOffsetI += charWidth;
            xOffsetF += ch.sWidth;
            // Put in map
            glyphs.put(c, ch);
        }

        // Flip image Horizontal to get the origin to bottom left
        AffineTransform transform = AffineTransform.getScaleInstance(1f, -1f);
        transform.translate(0, -image.getHeight());
        AffineTransformOp operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
        image = operation.filter(image, null);

        // Get char width & char height of image
        int width = image.getWidth();
        int height = image.getHeight();

        // Put pixel data into int[] pixels
        int[] pixels = new int[width * height];
        image.getRGB(0, 0, width, height, pixels, 0, width);

        // Put pixel data into byte buffer
        ByteBuffer buffer = MemoryUtil.memAlloc(width * height * 4);
        for (int i = 0; i < height; i++){
            for (int j = 0; j < width; j++) {
                // Pixel format : OxAARRGGBB
                int pixel = pixels[i * width + j];
                buffer.put((byte) ((pixel >> 16) & 0xFF));  // 0x000000RR
                buffer.put((byte) ((pixel >> 8) & 0xFF));   // 0x000000GG
                buffer.put((byte) ((pixel) & 0xFF));        // 0x000000BB
                buffer.put((byte) ((pixel >> 24) & 0xFF));  // 0x000000AA
                //buffer.put((byte)(0xFF));                 // Test
            }
        }

        buffer.flip(); // Set index back to 0

        Texture fontTexture = Texture.createTexture(width, height, buffer); // Create texture

        MemoryUtil.memFree(buffer); // Free buffer

        return fontTexture;


    }

    private BufferedImage createCharImage(char c) {
        // Begin by calculating proper width and height
        BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        if (Options.ANTIALIAS) {
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }
        g.setFont(font);
        FontMetrics fontMetrics = g.getFontMetrics();
        g.dispose();

        int charWidth = fontMetrics.charWidth(c);
        int charHeight = fontMetrics.getHeight();

        if (charWidth == 0) return null;

        // Now set up the image
        image = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB);
        g = image.createGraphics();
        if (Options.ANTIALIAS) {
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }
        g.setFont(font);
        g.setPaint(Color.WHITE); // Use white so we can set the color while rendering
        g.drawString(String.valueOf(c), 0, fontMetrics.getAscent()); // Paint char onto image
        g.dispose();

        // Finally return the final image
        return image;

    }

    public void drawText(Renderer renderer, CharSequence text, float x, float y, float scale, Colors c) {

        float xOffset = 0;
        float yOffset = 0;

        texture.prepare();
        //renderer.begin();

        for (int i = 0; i < text.length(); i++) {
            char ch = text.charAt(i);
            if (ch == '\n') {
                // If we start a new line, change the yOffset to reflect (then continue)
                yOffset -= fontHeight;
                continue;
            }
            if (ch == '\r') {
                // Skip carriage return
                continue;
            }
            Glyph g = glyphs.get(ch);
            renderer.drawTextureFromFont(texture, x + xOffset, y + yOffset, g.width * scale, g.height * scale, g.s, g.t, g.sWidth, g.sHeight, c);
            xOffset += g.width * scale;
        }

        //renderer.end();
        texture.unbind();

    }


}

Texture.java

public class Texture {

    // Store handle
    private final int id;

    private int width;
    private int height;

    private ByteBuffer image;

    // Draw texture using given renderer
    public void draw(Renderer renderer, float x, float y, float scale) {

        // Scale texture
        float w = ((float)width * scale * 2) / (Game.WIDTH);
        float h = ((float)height * scale * 2) / (Game.HEIGHT);

        renderer.drawTextureFromTexture(this, x, y, w, h);
    }

    // Create new texture
    public Texture() {
        id = glGenTextures();
    }

    // Prepare texture to be drawn
    public void prepare() {
        bind();
        uploadData(width, height, image);
    }

    // Bind texture
    public void bind() {
        glBindTexture(GL_TEXTURE_2D, id);
    }

    // Set parameter of texture
    private void setParameter(int name, int value) {
        glTexParameteri(GL_TEXTURE_2D, name, value);
    }

    // Upload image data with specified width and height
    public void uploadData(int width, int height, ByteBuffer data) {
        uploadData(GL_RGBA8, width, height, GL_RGBA, data);
    }

    // Upload image data with specified internal format, width, height, and image format
    public void uploadData(int internalFormat, int width, int height, int format, ByteBuffer data) {
        glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
    }

    // Delete texture
    public void delete() {
        glDeleteTextures(id);
    }

    // Get width
    public int getWidth() {
        return width;
    }

    // Set width
    public void setWidth(int width) {
        if (width > 0) {
            this.width = width;
        }
    }

    // Get height
    public int getHeight() {
        return height;
    }

    // Set height
    public void setHeight(int height) {
        if (height > 0) {
            this.height = height;
        }
    }

    // Set image
    public void setImage(ByteBuffer image) {
        this.image = image;
    }

    public static Texture createTexture(int width, int height, ByteBuffer image) {
        Texture texture = new Texture();
        texture.setWidth(width);
        texture.setHeight(height);
        texture.setImage(image);

        texture.prepare();

        return texture;
    }

    public static Texture loadTexture(String path) {
        ByteBuffer image;
        int width, height;
        try (MemoryStack stack = MemoryStack.stackPush()) {
            IntBuffer w = stack.mallocInt(1);
            IntBuffer h = stack.mallocInt(1);
            IntBuffer comp = stack.mallocInt(1);

            // Load image
            stbi_set_flip_vertically_on_load(true);
            image = stbi_load(path, w, h, comp, 4);
            if (image == null) throw new RuntimeException("Could not load texture.");
            width = w.get();
            height = h.get();
        }

        return createTexture(width, height, image);

    }

    public void unbind() {
        glBindTexture(GL_TEXTURE_2D, 0);
    }

}
java
opengl
lwjgl
asked on Stack Overflow Aug 23, 2017 by drewpel • edited Aug 23, 2017 by drewpel

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0