Question

Currently, I'm attempting to create a Camera class in LWJGL, but I've been running into a problem with the projection matrix. For some reason, when I try to multiply the vertices by the projection matrix, nothing at all appears on screen.

Camera class

public class Camera {

private Vector3f position, rotation;
private Matrix4f view;

private final Vector3f xAxis, yAxis, zAxis;

private float fov, aspect, zNear, zFar;
private Matrix4f projection;

public Camera(float fov, float aspect, float zNear, float zFar){
    this.fov = fov;
    this.aspect = aspect;
    this.zNear = zNear;
    this.zFar = zFar;

    projection = createPerspectiveProjection(fov, aspect, zNear, zFar);

    position = new Vector3f();
    rotation = new Vector3f();
    view = new Matrix4f();
    view.setIdentity();

    xAxis = new Vector3f(1, 0, 0);
    yAxis = new Vector3f(0, 1, 0);
    zAxis = new Vector3f(0, 0, 1);
}

public void addRotation(float x, float y, float z){
    rotation.x += x;
    rotation.y += y;
    rotation.z += z;

    apply();
}

public void move(float x, float y, float z){
    position.x += x;
    position.y += y;
    position.z += z;

    apply();
}

public Matrix4f getView(){
    return view;
}

public Matrix4f getProjection(){
    return projection;
}

private void apply(){
    view.setIdentity();

    view.rotate(rotation.x, xAxis);
    view.rotate(rotation.y, yAxis);
    view.rotate(rotation.z, zAxis);

    view.translate(position);
}

private Matrix4f createPerspectiveProjection(float fov, float aspect, float zNear, float zFar){
    Matrix4f mat = new Matrix4f();

    float yScale = (float) (1 / (Math.tan(Math.toRadians(fov / 2))));
    float xScale = yScale / aspect;
    float frustrumLength = zFar - zNear;

    mat.m00 = xScale;
    mat.m11 = yScale;
    mat.m22 = -((zFar + zNear) / frustrumLength);
    mat.m23 = -1;
    mat.m32  = -((2 * zFar * zNear) / frustrumLength);
    mat.m33 = 0;

    return mat;
}

}

Main class

public class Game implements Runnable {

public static final int WIDTH = 800;
public static final int HEIGHT = 600;

public static final DisplayMode dm = new DisplayMode(WIDTH, HEIGHT);

int vaoID;

ShaderProgram program;
Camera camera;

Model model;

public Game(){
    new Thread(this).start();
}

public void run(){
    init();

    while(true){
        if(Display.isCloseRequested())
            break;

        update(Timer.getElapsedTime());

        render();

        Display.sync(60);
        Display.update();
    }

    Display.destroy();
}

public void init(){
    try{
        Display.setTitle("Ludum Dare!");
        Display.setDisplayMode(dm);
        Display.create();
    }catch(LWJGLException e){
        e.printStackTrace();
        System.exit(1);
    }

    Timer.start();

    camera = new Camera(60.0f, WIDTH / HEIGHT, 0.1f, 100.0f);

    program = new ShaderProgram("res/shader/defaultshader.vert", "res/shader/defaultshader.frag");

    glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

    vaoID = glGenVertexArrays();
    glBindVertexArray(vaoID);

    model = new Model(new float[] {
        -1, -1, 1,
        1, -1, 1,
        0, 1, 1
    });
}

public void update(float delta){

}

public void render(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    program.bind();

    program.setUniform("model_matrix", model.getModel());
    program.setUniform("view_matrix", camera.getView());
    program.setUniform("projection_matrix", camera.getProjection());

    model.render();

    program.unbind();
}

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

}

Vertex Shader

#version 330 core

layout(location = 0) in vec3 vertex_modelspace;

uniform mat4 model_matrix;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;

void main(){
    mat4 modelviewprojection_matrix = projection_matrix * view_matrix * model_matrix;
    vec4 vertex = vec4(vertex_modelspace, 1.0);

    gl_Position = modelviewprojection_matrix * vertex;
}

ShaderProgram class

public class ShaderProgram {

private int vertexShaderID, fragmentShaderID;
private int programID;

public ShaderProgram(String vertPath, String fragPath){
    programID = glCreateProgram();

    vertexShaderID = attachShader(vertPath, GL_VERTEX_SHADER);
    fragmentShaderID = attachShader(fragPath, GL_FRAGMENT_SHADER);
    link();
}

private int attachShader(String path, int type){
    StringBuilder shaderSource = new StringBuilder();

    try{
        BufferedReader reader = new BufferedReader(new FileReader(new File(path)));
        String line;

        while((line = reader.readLine()) != null){
            shaderSource.append(line).append("\n");
        }

        reader.close();
    }catch(IOException e){
        e.printStackTrace();
        System.out.println("Error reading from shader " + path);
        System.exit(1);
    }

    System.out.println("Compiling shader " + path);
    int id = glCreateShader(type);
    glShaderSource(id, shaderSource);
    glCompileShader(id);

    if(glGetShaderi(id, GL_COMPILE_STATUS) == GL_FALSE){
        System.out.println(glGetShaderInfoLog(id, 1000));
        System.exit(1);
    }

    return id;
}

private void link(){
    System.out.println("Linking program...");
    glAttachShader(programID, vertexShaderID);
    glAttachShader(programID, fragmentShaderID);
    glLinkProgram(programID);
}

public void bind(){
    glUseProgram(programID);
}

public void unbind(){
    glUseProgram(0);
}

public void setUniform(String name, Matrix4f value){
    FloatBuffer matrix = BufferUtils.createFloatBuffer(16);
    value.store(matrix); matrix.flip();
    glUniformMatrix4(glGetUniformLocation(programID, name), false, matrix);
}

}

When I run this, the VBO that I create (A triangle) doesn't appear, but when I leave the projection matrix out of the multiplication in the vertex shader, it runs just fine. Am I missing something?

Was it helpful?

Solution

I'm making the assumption that model.getModel() will return the identity matrix (just like your view matrix is identity). In that case, you have the following situation: You draw a triangle in the plane z=1.0. If you use identity as projection matrix also, you directly draw in clip space, and the trianlge will be on the far plane, so it is visible.

However, your createPerspectiveProjection function seems to be written with the standard OpenGL conventions in mind, so it is almost like what glFrustum(). (Your code is missing the parts which are labeled A and B in that manpage, so you are limited to a symmetric frustum, but that is fine in most cases.) The conventions used for this matrix were that the camera is looking into the -z direction, and the zNear and zFar parameters are actually maped such that a point at z_eye=-zNear is projected to the near plane (z_ndc=-1), and a point at z_eye=-zFar is projected to the far plane (z_ndc=1). So your triangle at z=1 is just behind the camera, if you apply that projection matrix.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top