2
votes

I am trying to add a paralax effect to an existing engine. So far the engine worked with an orthogonal projection. Objects are placed in pixel coordinates on the screen. The problem is that I can not figure out how to replicate the same projection with a perspective projection matrix ect. that I can add a Z coordinate for depth.

I tried various combinations of matrices and z coordinates already and the result was always a black screen.

The matrix I am trying to replace:

glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(1280.0f), static_cast<GLfloat>(720.0f), 0.0f, 0.0f, -100.0f);

The vertex shader:

// Shader code (I tested this while having identity matrices for view and model

#version 330 core

layout (location = 0) in vec2 vertex;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

void main() {
    gl_Position = projection * view * model * vec4(vertex.xy, 1.0f, 1.0f);
}

The projection code I thought might work:

    glm::mat4 model = glm::mat4(1.0f);
    model = glm::translate(model, glm::vec3(-640, -310.0f, 0.0f));
    model = glm::scale(model, glm::vec3(1.0f / 1280.0f, 1.0f / 720.0f, 1.0f));

    glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 0.0f);
    glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
    glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
    glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); 
    glm::mat4 projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, -100.0f);

Expected Is that a rectangle gets still displayed at similar position (I can correct the details once something works) without having a black screen.

2
"The problem is that I can not figure out how to replicate the same projection with a perspective projection matrix." That is impossible. You cannot replacate an ortho projection by a perspective one. You could just select one particular plane where both projections would yield the same image.derhass
It is possible... you may be confusing the fact that the scale effect into depth is wanted. Saying paralax scrolling and smaller in the distance? But the coordinate system of the matrices is different the orthogonal one can work with pixel coordinates and the projection may require to scale the coordinates into its coordinate space, thats why I added the model matrix. So the closest plane in the camera at specified coordinates should be the one you mentioned, but with different z coordinates objects should be placed in the distance, like expected.leechmasterb
That was exactly what I meant when I wrote "for one particular plane".derhass
@leechmasterb It is only possible if the object is 2 dimensional and located in a plan that is parallel to the xy plane of the view space. In any other case it is impossible.Rabbid76
@derhass The idea is to have planes facing the camera with different Z coordinates, like in any basic 2d engine. But using a projection camera for paralax effects. There will not be any camera rotation anyhow. So basically you summarized what I am trying to do if not missunderstood. The question is how to get it working while dealing with different coordinate systems. -1,1 on the x coordinates vs. 0,1280 for example and so on.leechmasterb

2 Answers

2
votes

The specification of the Perspective projection matrix is wrong.

glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, -100.0f);

glm::perspective defines a Viewing frustum by an field of view angle along the y axis, an aspect ratio and a distance to the near and the far plane.
So the near and the far plane have to be positive values (> 0) and near has to be less than far:

0 < near < far

e.g.:

glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);

The geometry has to be in between the near and the far plane, else it is clipped.
The ration of the size of the projected area and the depth is linear and can be calculated. It depends on the field of view angle:

float fov_y = glm::radians(45.0f);
float ratio_size_depth = tan(fov_y / 2.0f) * 2.0f;

Note, if an object should appear with half the size in the projection on the viewport, the distance from the object to the camera (depth) has to be doubled.

2
votes

So the corrected model translation matrix and required depth in the shader to have the coordinates match on the plane are as follows:

int width = 1280.0f;
int height = 720.0f;

glm::mat4 model = glm::mat4(1.0f);  
model = glm::scale(model, glm::vec3(-1.0f / width, -1.0f / height, 1.0f));
model = glm::translate(model, glm::vec3(-((float)width / 2.0f), -((float)height / 2.0f), 0.0f));

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); 
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);

Shader with Z-Value:

#version 330 core

layout (location = 0) in vec2 vertex;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

void main() {
    gl_Position = projection * view * model * vec4(vertex.xy, 1.208f, 1.0f);
}

Which will be equivalent tho this orthogonal matrix:

glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(this->width), static_cast<GLfloat>(this->height), 0.0f, 0.0f, -100.0f);

The matrices can also be multiplied together to have only one projection matrix you pass to the shader. This will make it easier to have an actual model matrix passed with the mesh ect.