Main Menu

Projection Matrix

Started by chris1, May 25, 2020, 01:00:18

Previous topic - Next topic

chris1

Hello

I wish to display a rectangle in OpenGL by passing vertex coordinates and the MVP matrix into the vertex shader (then fragment shader).
My rectangle vertices are the usual (-0.5, 0.5, 0), (0.5, 0.5, 0), (-0.5, -0.5, 0) and (0.5, -0.5, 0)
My model matrix is 4x4 identity.

My camera sits at (0,0,1) and has the rotation of (0,0,0) (pitch, yaw, roll) so looks towards the negative z-axis.
Hence my view matrix is
(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, -1,
0, 0, 0, 1)

Setting the MVP matrix to view * model produces a coloured rectangle in correct NDC coordinates.

However, when I introduce the projection matrix - generated by .setPerspective(3.14159 / 2, width / height, 0.1, 100) - and set MVP = projection * view * model, the rectangle disappears entirely.
Passing data is not the issue since if my projection matrix is also an identity, the rectangle reappears.

I have no idea what I am doing wrong. I have tried numerous implementations of the projection matrix which always resulted in producing no graphical output (which raises the suspicion of the fault not lying in the projection matrix...)

Here is my vertex shader:
#version 330 core

in vec3 in_Position;

uniform mat4 u_MVP;

out vec3 colour;

void main(void){

    gl_Position = u_MVP * vec4(in_Position, 1.0);
    colour = vec3((in_Position.x+1) / 2, 1, (in_Position.y+1) / 2);

}


And my fragment shader:
#version 330 core

in vec3 colour;

out vec4 out_Colour;

void main(void){

    out_Colour = vec4(colour, 1.0);

}


My render function:
                        //enable shaders & bind to rectangle data
                        shader.start();
                        glBindVertexArray(vaos.get(0));
                        
                        //MVP
                        Matrix4f model = shape.getModelTransform(); //assume identity
                        Matrix4f view = camera.getViewTransform(); //assume translation -1 in z
                        Matrix4f projection = new Matrix4f();
                        projection.setPerspective((float) Math.PI / 2, (float) width / (float) height, 0.1f, 100f);
                        Matrix4f transform = projection.mul(view.mul(model));

                        //pass MVP & render indexed vertices
                        glUniformMatrix4fv(u_MVP, false, transform.get(BufferUtils.createFloatBuffer(16)));
                        glDrawElements(GL_TRIANGLES, shape.getPoints().length * 3, GL_UNSIGNED_INT, 0);
                        
                        //close renderer
                        glBindVertexArray(0);
                        shader.stop();


If someone could please clarify how to implement the correct projection matrix or point out other mistakes I have made, I'd be grateful.

P.S.
When P is identity, a camera translation actually results in the rectangle being distorted but I believe that's because there is no correct P matrix to rectify it. Correct me if I'm wrong

KaiHH

A simple MCVE would be nice. The following does work for me:
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.NULL;
import java.nio.FloatBuffer;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL;
public class OrthoDemo {
    public static void main(String[] args) {
        glfwInit();
        int width = 1200, height = 600;
        long window = glfwCreateWindow(width, height, "", NULL, NULL);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
        glfwMakeContextCurrent(window);
        GL.createCapabilities();
        FloatBuffer fb = BufferUtils.createFloatBuffer(16);
        while (!glfwWindowShouldClose(window)) {
            glClear(GL_COLOR_BUFFER_BIT);
            Matrix4f model = new Matrix4f();
            Matrix4f view = new Matrix4f().translation(0, 0, -1);
            Matrix4f projection = new Matrix4f();
            projection.setPerspective((float) Math.PI / 2, (float)width/height, 0.1f, 100f);
            glLoadMatrixf(projection.mul(view.mul(model)).get(fb));
            glBegin(GL_QUADS);
            glVertex3f(-.5f, -.5f, 0);
            glVertex3f(+.5f, -.5f, 0);
            glVertex3f(+.5f, +.5f, 0);
            glVertex3f(-.5f, +.5f, 0);
            glEnd();
            glfwSwapBuffers(window);
            glfwPollEvents();
        }
    }
}

Also, remember that JOML objects don't behave like immutable value objects. Every operation will modify `this` or an explicit `dest` argument. So, your:
Matrix4f transform = projection.mul(view.mul(model));

will modify `view`, which could be a problem iff `camera.getViewTransform();` always returns the same matrix instance without initializing it every call and `model` is not the identity matrix.

chris1

Forgive me if I misunderstand, but:
If you notice, my code utilises shaders. I will want to still use shaders.
Your solution does not load any data explicitly into the GPU, while I need to use the VAOs set up for every object.
I would also like to use the glDrawElements method since the vertex data is stored in the VAOs that I bind to.

(Sorry for not specifying the VAO in the first question, I didn't think the problem was extended to them.)

KaiHH

Nothing of the points you mentioned is relevant to the matrices working or not. I had a look at your shaders and they were fine. So, for simplicity I just used the FFP with glLoadMatrix, which btw. does upload the matrix. Also, whether you use VAOs or not (the default VAO) is irrelevant to your case. It does not matter how you do vertex specification.

In any way: A simple, complete and reproducible code would be best here to move further.

chris1

Forgive my ignorance, I'm just trying to understand.

Your first reply contains instructions on how to construct the projection matrix, I think the thread's question has been answered; perhaps the next question belongs to another board... but:

Since I am storing model data in a VAO, shouldn't my rendering function include the use of that VAO? Same with the shader associated with the object to render? Typically, I think, most render functions follow the structure I have: enable shader, bind VAO, upload uniform data, draw call, unbind VAO, disable shader. These I have seen in code examples and they seem to be able to render objects properly, except mine which is doing something wrong.
If I was to use your rendering method, how would I go about specifying which attribute each glVertex call refers to (assuming I would later add more attributes to every model)? The same question for other uniform data. If I was going to call glVertex iteratively, would there even still be a point of loading the model into a VAO?

Additionally, is there any reason why using glVertex is advantageous to glUniformMatrix & glDrawElements (besides it working of course)?

In short: I have a system which can contain polygons of any shape, each polygon is assigned a VAO and a shader. In a render call for all shapes in the program, I want each shape to use its own VAO and shader.
Obviously, I'm not expecting an answer containing a thousand lines of code, but the general idea is specified in the render function I gave in the original question.

Thanks

KaiHH

What I understood from your initial post:
1. when you render things without a projection matrix, then thinks work
2. when you introduce a projection matrix (with settings that should work), then things don't work anymore

Now my goal here was to identify what you probably might have done wrong between steps 1 and 2, or, though less likely but still possible: what JOML might have done wrongly which need to be fixed (I am the author of JOML by the way.)

When the original problem with the projection matrix has been resolved for you, then I am curious how actually.

About whether to use the fixed-function pipeline or VBOs/VAOs: You should _never_ use the fixed-function pipeline like I did.
The sole reason I was using that is to make the example code simpler without laying the focus on the vertex specification and shader setup but on the actual matrix building code.
There is no advantage to using glVertex and other fixed-function pipeline functions over VAOs and shaders other than simplicity.

The way you described the basic structure of a render call is about right: bind VAO, bind shader, set shader uniforms, issue draw* call, unbind.

chris1

Still, the GUI presents no shape.
I assumed my projection matrix construction would be clarified in this thread, and it has. Yet the result is still false.
How can make your job easier? Which pieces of code should I post if any?
Should I raise the render issue on a different board?

abcdef

Download renderdoc, run your program through it, look at the actual gl_position values your shader gets and see if it's in the NDC

Also print out your MVP matrix and do the calculation by hand to see what should be happening outside the shader.

I am sure it will be obvious what the error is once you see, just debug with the right tools

My first guess is your matrix moves the points outside the frustrum. Kai has kindly shown you that it should work with his example, so you have a bug somewhere.

chris1

I actually sorted it out an hour ago, was going to post here soon.

My model and view matrices were transposed.
I didn't see it until I went through every transformation matrix.

It's properly working now. Thanks to all replies.