Search code examples
javaopenglshaderlwjgl

LWJGL OpenGL Uniforms aren't being set


Right now I'm on the simple step of rendering objects. I am following the gitbook here, and although I have pretty much the same setup, I get nothing on the screen.

I have tried:

  • Double checking my view matrix math (which was actually wrong, but was fixed)
  • Tried rendering without the use of shaders (which worked fine)
  • Tried rendering with shaders, but without uniforms (works fine)

But as soon as I use the uniform, I get nothing on the screen, besides my clear color. Any help with how to get these wack uniforms working would be greatly appreciated. Thanks!

My code for my Application Window class:

package flaff.gameengine.internal;

import flaff.gameengine.*;

import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import static org.lwjgl.opengl.GL30.*;

public class ApplicationWindow
{
    private int width;
    private int height;
    private String title;
    private boolean resized;
    private long window;

    public ApplicationWindow(int width, int height, String title)
    {
        this.width = width;
        this.height = height;
        this.title = title;
    }

    public void start()
    {
        if (!GLFW.glfwInit())
        {
            System.err.println("Error: Couldn't initialize GLFW");
            System.exit(-1);
        }
        this.window = GLFW.glfwCreateWindow(this.width, this.height, this.title, 0, 0);
        if (window == 0)
        {
            System.err.println("Error: Window couldn't be created");
            System.exit(-1);
        }
        GLFW.glfwMakeContextCurrent(this.window);
        GL.createCapabilities();
        GLFW.glfwSetFramebufferSizeCallback(this.window, (unusedW, width, height) ->
        {
            this.width = width;
            this.height = height;
            this.resized = true;
        });
        glClearColor(0.1921569f, 0.3019608f, 0.4745098f, 0f);
        GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
        // centers the window
        GLFW.glfwSetWindowPos(this.window, (videoMode.width() - this.width) / 2, (videoMode.height() - this.height) / 2);
        // show the video
        GLFW.glfwShowWindow(this.window);
        glEnable(GL_DEPTH_TEST);
    }

    public void render()
    {
        if (this.resized)
        {
            glViewport(0, 0, width, height);
            glMatrixMode(GL_MODELVIEW);
            this.resized = false;
        }

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        List<Renderer> rendererList = GObject.findObjectsOfType(Renderer.class);
        for (int index = 0; index < rendererList.size(); index++)
            rendererList.get(index).render();
    }

    public boolean isClosed()
    {
        return GLFW.glfwWindowShouldClose(this.window);
    }

    public void nextFrame()
    {
        // non-relevant code
        GLFW.glfwPollEvents();

        // more non-relevant code

        // render loop
        this.render();

        // more non-relevant code

        GLFW.glfwSwapBuffers(this.window);
    }
}

My Code for my Instance Class:

package flaff.gameengine;

import java.util.List;
import flaff.gameengine.internal.*;
import flaff.gameengine.renderers.MeshRenderer;

public class Instance
{
    private ApplicationWindow window;

    private Instance(int width, int height, String title)
    {
        this.window = new ApplicationWindow(width, height, title);
    }
    public ApplicationWindow getWindow()
    {
        return this.window;
    }
    public void run()
    {
        this.window.start();
        Shader shader = Shader.getDefault();
        shader.createUniform("projectionMatrix");
        MeshRenderer renderer = new MeshRenderer();
        renderer.setShader(shader);
        Mesh mesh = new Mesh(new float[] {
            0.0f,  0.5f, 0f,
            -0.5f, -0.5f, 0f,
            0.5f, -0.5f, 0f
        });
        renderer.setMesh(mesh);
        while (!this.window.isClosed())
        {
            this.window.nextFrame();
        }
        // non-relevant code
    }

    // entry point
    public static void main(String[] args)
    {
        new Instance(800, 600, "Test").run();
    }
}

Mesh Renderer:

package flaff.gameengine.renderers;

import flaff.gameengine.Application;
import flaff.gameengine.Mesh;
import flaff.utils.geometry.Mathg;
import flaff.utils.geometry.matrix.Matrix4x4f;

public class MeshRenderer extends Renderer
{
    private Mesh mesh;
    private Matrix4x4f perspectiveMatrix;
    
    public MeshRenderer() 
    {
        super();
    }
    
    public void setMesh(Mesh value)
    {
        this.mesh = value;
    }
    
    @Override
    public void render()
    {
        float aspectRatio = (float)Application.getWidth() / Application.getHeight();
        float fov = Mathg.DEG2RAD * 90F;
        float zNear = 0.001F;
        float zFar = 1000F;
        
        if (this.perspectiveMatrix == null)
            this.perspectiveMatrix = Matrix4x4f.createPerspectiveFieldOfView(aspectRatio, fov, zNear, zFar);
        
        shader.bind();
        
        shader.setUniform("projectionMatrix", this.perspectiveMatrix);
        
        glBindVertexArray(mesh.getVaoId());
        glEnableVertexAttribArray(0);
        
        glDrawArrays(GL_TRIANGLES, 0, mesh.getVertexCount());

        // Restore state
        glDisableVertexAttribArray(0);
        glBindVertexArray(0);
        
        shader.unbind();
    }
}

Renderer Class:

package flaff.gameengine.renderers;

import flaff.gameengine.Behaviour;
import flaff.gameengine.SerializeField;
import flaff.gameengine.internal.Shader;

public abstract class Renderer extends Behaviour
{
    @SerializeField
    protected Shader shader;
    
    public Renderer()
    {
        super();
    }
    
    public void setShader(Shader shader)
    {
        this.shader = shader;
    }
    
    public abstract void render();
}

Shader Class:

package flaff.gameengine.internal;

import static org.lwjgl.opengl.GL30.*;

import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Map;

import org.lwjgl.system.MemoryStack;

import flaff.utils.geometry.matrix.Matrix4x4f;

public class Shader
{
    private final int programId;
    private int vertexShaderId;
    private int fragmentShaderId;
    private Map<String, Integer> uniforms;
    
    public Shader(String vertexShader, String fragmentShader)
    {
        this.uniforms = new HashMap<String, Integer>();
        
        this.programId = glCreateProgram();
        if (programId == 0)
            throw new RuntimeException("FML");
        
        this.vertexShaderId = this.createShader(vertexShader, GL_VERTEX_SHADER);
        this.fragmentShaderId = this.createShader(fragmentShader, GL_FRAGMENT_SHADER);
        
        this.link();
    }
    
    private int createShader(String shaderCode, int shaderType)
    {
        int shaderId = glCreateShader(shaderType);
        if (shaderId == 0)
            throw new RuntimeException("Error creating a shader. FML: " + shaderType);
        
        glShaderSource(shaderId, shaderCode);
        glCompileShader(shaderId);
        
        if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0)
            throw new RuntimeException("Error compiling Shader code: " + glGetShaderInfoLog(shaderId, 1024));
        
        glAttachShader(this.programId, shaderId);
        
        return shaderId;
    }
    
    public void link()
    {
        glLinkProgram(this.programId);
        if (glGetProgrami(this.programId, GL_LINK_STATUS) == 0)
            throw new RuntimeException("Error linking Shader code: " + glGetProgramInfoLog(programId, 1024));
        
        if (this.vertexShaderId != 0)
            glDetachShader(this.programId, this.vertexShaderId);
        
        if (this.fragmentShaderId != 0)
            glDetachShader(programId, this.fragmentShaderId);
        
        glValidateProgram(this.programId);
        if (glGetProgrami(this.programId, GL_VALIDATE_STATUS) == 0)
            System.err.println("Warning validating Shader code: " + glGetProgramInfoLog(programId, 1024));
    }
    
    public void bind()
    {
        glUseProgram(this.programId);
    }
    
    public void unbind()
    {
        glUseProgram(0);
    }
    
    public void cleanup()
    {
        unbind();
        if (this.programId != 0)
            glDeleteProgram(this.programId);
    }
    
    public void createUniform(String uniformName)
    {
        int uniformLocation = glGetUniformLocation(this.programId, uniformName);
        if (uniformLocation < 0)
            throw new RuntimeException("Could not find uniform: " + uniformName);
        
        uniforms.put(uniformName, uniformLocation);
    }
    
    public void setUniform(String uniformName, Matrix4x4f matrix)
    {
        try (MemoryStack stack = MemoryStack.stackPush())
        {
            FloatBuffer fb = stack.mallocFloat(16);
            matrix.updateBuffer(fb);
            
            glUniformMatrix4fv(uniforms.get(uniformName), false, fb);
        }
    }
    
    public static Shader getDefault()
    {
        return new Shader(
                  "#version 330\n"
                + "\n"
                + "\n"
                + "layout (location=0) in vec3 position;\n"
                + "\n"
                + "uniform mat4 projectionMatrix;\n"
                + "\n"
                + "void main()\n"
                + "{\n"
                + "    gl_Position = projectionMatrix * vec4(position, 1.0);\n"
                + "}", 
    
                  "#version 330\n"
                + "\n"
                + "\n"
                + "out vec4 fragColor;\n"
                + "\n"
                + "void main()\n"
                + "{\n"
                + "    fragColor = vec4(0.5, 0.5, 0.5, 1.0);\n"
                + "}");
    }
}

Mesh Class:

package flaff.gameengine;

import java.nio.FloatBuffer;
import static org.lwjgl.opengl.GL30.*;
import org.lwjgl.system.MemoryUtil;

public class Mesh extends GObject
{
    private final int vaoId;

    private final int vboId;

    private final int vertexCount;

    public Mesh(float[] positions) //TODO: Implement indices
    {
        FloatBuffer verticesBuffer = null;
        verticesBuffer = MemoryUtil.memAllocFloat(positions.length);
        vertexCount = positions.length / 3;
        verticesBuffer.put(positions).flip();

        vaoId = glGenVertexArrays();
        glBindVertexArray(vaoId);

        vboId = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboId);
        glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);          
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        MemoryUtil.memFree(verticesBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glBindVertexArray(0);
    }

    public int getVaoId() 
    {
        return vaoId;
    }

    public int getVertexCount() 
    {
        return vertexCount;
    }

    private void onDestroy() 
    {
        glDisableVertexAttribArray(0);

        // Delete the VBO
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glDeleteBuffers(vboId);

        // Delete the VAO
        glBindVertexArray(0);
        glDeleteVertexArrays(vaoId);
    }
}

Solution

  • The geometry (triangle) is clipped by the near plane of the Perspective Projection. With Perspective Projection the viewing volume is a Frustum. Any geometries that are not in the viewing volume and not between the near and far planes are clipped.

    Your geometry (triangle) is drawn at (z=0.0). The near pane is 0.001 and the dat plane is 1000. Therefor the geometry is clipped. Shift the geometry along the negative z-axis:

    Mesh mesh = new Mesh(new float[] {
         0.0f,  0.5f, -5.0f,
        -0.5f, -0.5f, -5.0f,
         0.5f, -0.5f, -5.0f
     });
    

    Note that the view space coordinate system is a right-handed system. The x-axis points to the right, the y-axis points upwards. Therefore, the z-axis points out of the viewport. To move the geometry between the near and far planes, you must move the geometry in the negative direction along the z-axis.
    The projection matrix converts from the view space to the clip space. Clip space is a right-handed system. For the transformation from a left-handed system to a right-handed system, the projection matrix "flips" the z-axis.