OpenGL sprite rotation to camera - c++

I am using OpenGL 3.3. It is necessary to make the sprite always look at the camera.
In most cases, the code is from the learnOpenGL lessons, I rework it to fit my needs. I tried in different ways, but so far I have not managed to do something that works adequately.
The projection is perspective.
glm::mat4 projection = glm::perspective(glm::radians(fov), (float)width_ / (float)height_, 0.1f, 30.0f);
I draw the sprite like this.
void SpriteRenderer3D::DrawSprite(const Texture2D& texture, glm::vec3 position, glm::vec3 camera_pos, glm::vec3 size, float rotate, glm::vec3 color) {
// activate shader
shader.Use();
// create transformations
glm::mat4 model = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
model = glm::translate(model, glm::vec3(position)); // first translate (transformations are: scale happens first, then rotation, and then final translation happens; reversed order)
model = glm::translate(model, glm::vec3(0.5f * size.x, 0.5f * size.y, 0.5f * size.z)); // move origin of rotation to center of quad
model = glm::rotate(model, glm::radians(rotate), glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::translate(model, glm::vec3(-0.5f * size.x, -0.5f * size.y, -0.5f * size.z)); // move origin back
model = glm::scale(model, glm::vec3(size)); // last scale
this->shader.SetMatrix4("model", model);
// render textured quad
this->shader.SetVector3f("spriteColor", color);
glActiveTexture(GL_TEXTURE0);
texture.Bind();
}
I use this Vshader.
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
TexCoords = vec2(aTexCoord.x, aTexCoord.y);
}
The view matrix is ​​considered this way when you move the camera.
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
ResourceManager::GetShader("sprite3d").SetMatrix4("view", view);

Related

Why lighting appears on opposite side?

I am trying to build lighting using this tutorial. However, lighting appears on wrong side of human object and I do not know why.
Normals were created per triangle. Vertices of a triangle basically have the same normal:
glm::vec3 calculateNormal(glm::vec3 vertice_1, glm::vec3 vertice_2, glm::vec3 vertice_3)
{
glm::vec3 vector_1 = vertice_2 - vertice_1;
glm::vec3 vector_2 = vertice_3 - vertice_1;
return glm::normalize(glm::cross(vector_1, vector_2));
}
Here is code for vertex shader:
#version 330 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 normal;
out vec4 vert_color;
out vec3 Normal;
out vec3 FragPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 transform;
uniform vec4 color;
void main()
{
vert_color = color;
gl_Position = projection * view * model * transform * vec4(pos.x, pos.y, pos.z, 1.0);
FragPos = vec3(model * transform * vec4(pos, 1.0));
Normal = normal;
}
Fragment shader:
#version 330 core
uniform vec3 cameraPos;
uniform vec3 lightPos;
uniform vec3 lightColor;
in vec4 vert_color;
in vec3 FragPos;
in vec3 Normal;
out vec4 frag_color;
void main()
{
float ambientStrength = 0.1;
float specularStrength = 0.5;
vec3 ambient = ambientStrength * lightColor;
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(Normal, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
vec3 viewDir = normalize(cameraPos - FragPos);
vec3 reflectDir = reflect(-lightDir, Normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * vec3(vert_color.x, vert_color.y, vert_color.z);
frag_color = vec4(result, vert_color.w);
}
Main loop:
wxGLCanvas::SetCurrent(*glContext);
glClearDepth(1.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glm::mat4 model, view, projection;
model = glm::translate(model, modelPos); // modelPos is
view = fpsCamera->getViewMatrix();
projection = fpsCamera->getProjectionMatrix(windowWidth, windowHeight);
color = glm::vec4(0.310f, 0.747f, 0.185f, 1.0f);
glm::vec3 lightPos = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
glm::mat4 phantomtTransformation;
phantomtTransformation = glm::rotate(phantomtTransformation, - glm::pi<float>() / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
phantomtTransformation = glm::rotate(phantomtTransformation, - glm::pi<float>() , glm::vec3(0.0f, 0.0f, 1.0f));
ShaderProgram shaderProgram;
shaderProgram.loadShaders("Shaders/phantom.vert", "Shaders/phantom.frag");
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
shaderProgram.use();
shaderProgram.setUniform("transform", phantomtTransformation);
shaderProgram.setUniform("model", model);
shaderProgram.setUniform("view", view);
shaderProgram.setUniform("projection", projection);
shaderProgram.setUniform("color", color);
shaderProgram.setUniform("lightColor", lightColor);
shaderProgram.setUniform("lightPos", lightPos);
shaderProgram.setUniform("cameraPos", fpsCamera->getPosition());
glStencilMask(0xFF); // Write to stencil buffer
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 0, 0xFF); // Set any stencil to 0
glStencilFunc(GL_ALWAYS, 1, 0xFF); // Set any stencil to object ID
m_pantomMesh->draw();
glStencilFunc(GL_ALWAYS, 0, 0xFF); // Set any stencil to 0 // no need for testing
glFlush();
wxGLCanvas::SwapBuffers();
View from front of the object:
View from back of the object:
EDIT:
In order to debug I removed object rotation matrix from main loop:
glm::mat4 phantomtTransformation;
phantomtTransformation = glm::rotate(phantomtTransformation, - glm::pi<float>() / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
phantomtTransformation = glm::rotate(phantomtTransformation, - glm::pi<float>() , glm::vec3(0.0f, 0.0f, 1.0f));
shaderProgram.setUniform("transform", phantomtTransformation);
and changed line in fragment shader from
frag_color = vec4(result, vert_color.w);
to
frag_color = vec4(Normal, vert_color.w);
in order to visualize Normal values. As a result I noticed that when camera changes position phantom also changes color which means that normal values are also changing.
I think the cause of your problem is that you are not applying your model transformation to your normal vectors. Since you definitely do not want to skew them, you will have to create a special matrix for your normals.
As is explained further down the tutorial that you mentioned, the matrix can be constructed like this
Normal = mat3(transpose(inverse(model))) * aNormal;
in your vertex shader.
However, I highly recommend that you calculate the matrix in your application code instead, since you would calculate it per vertex in the above example.
Since you are using the glm library, it would look like this instead:
glm::mat3 model_normal = glm::mat3(glm::transpose(glm::inverse(model)));
You can then load your new model_normal matrix into the shader as a uniform mat3.

shape distortion when rotating model matrix

I'm attempting to rotate a shape using the glm library in conjunction with openGL. Unfortunately, when I attempt to rotate the shape using glm::rotate(), my shape's dimensions become stretched. Alongside this, the shape rotates around a seemingly random point. Everything is 2 dimensional.
Vertex Shader Code:
#version 330 core
layout(location = 0) in vec2 pos;
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;
void main()
{
gl_Position = projection * view * model * vec4(pos.x, pos.y, 1.0f, 1.0f);
}
Model Matrix Code:
m_angle = angle;
m_modelMatrix = glm::rotate(glm::mat4(1.0f), glm::radians((float)m_angle), glm::vec3(0, 0, 1));
Projection and View Matrices Code:
glm::mat4 proj = glm::mat4(1.0f);
glm::mat4 view = glm::mat4(1.0f);
proj = glm::perspective(glm::radians(53.f), 1.f, 0.1f, 100.f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
The issue is caused because the projection matrix doesn't take care of the aspect ration of the viewport. In your code the aspect parameter is 1.0:
proj = glm::perspective(glm::radians(53.f), 1.f, 0.1f, 100.f);
The aspect parameter has to to be the ratio of the width and height of the viewprot respectively window:
float aspect = (float)window_width/(float)window_height;
proj = glm::perspective(glm::radians(53.f), aspect, 0.1f, 100.f);

Switching from orthogonal to perspective projection

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.
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.
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.

Changing the angle in glm::perspective reflects the object

I was drawing 2 cubes on the screen and I realized that my object behaves really weird when I changed the angle in the perspective matrix, here is my code
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(programID);
GLuint mvp = glGetUniformLocation(programID, "MVP");
glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 10.0f);
glm::mat4 translate = glm::translate(glm::mat4(1.0f), vec3(-2.0f, 0.0f, -5.0f));
glm::mat4 rotate = glm::rotate(glm::mat4(1.0f), 54.0f, vec3(-1.0f, 0.0f, 0.0f));
glm::mat4 MVP = Projection * translate * rotate;
glUniformMatrix4fv(mvp, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, numOfIndices, GL_UNSIGNED_SHORT, nullptr);
translate = glm::translate(glm::mat4(1.0f), vec3(3.0f, 0.0f, -6.0f));
rotate = glm::rotate(glm::mat4(1.0f), 54.0f, vec3(0.0f, 1.0f, 0.0f));
MVP = Projection * translate * rotate;
glUniformMatrix4fv(mvp, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, numOfIndices, GL_UNSIGNED_SHORT, nullptr);
The 2 cubes share the same projection matrix but different translate and rotation matrix. Here is my shader
#version 430
in layout(location=0) vec3 position;
in layout(location=1) vec3 color;
uniform mat4 MVP;
out vec3 theColor;
void main(){
gl_Position = MVP * vec4(position,1.0f);
theColor = color;
}
the shader just times the MVP matrix by the position vertex. When I ran the code with 45.0f for degree in perspective matrix I got this:
and then when I ran it with 55.0f I got this:
it seems like I got behind the object and looks at them from there, and when I did 50.0f it closed up and I can only see the corner of one of the cube.
Okay I figured it out, glm::perspective takes radian as it's first argument so I should've wrote (3.14f/180.0f) * 45.0f rather than just 45.0f

Setting up a MVP Matrix in OpenGL

i'm trying to learn the basics of OpenGL, but i have a Problem with setting up the transformation matrices. I made the model, view and projection matrices, but i have a problem sending them to my vertex shader.
Here is the code:
//Set up MVP
glm::mat4 model = glm::mat4();
GLint uniModel = glGetUniformLocation(program, "model");
glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glm::mat4 view = glm::lookAt(
glm::vec3(2.5f, 2.5f, 2.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 0.0f, 1.0f));
GLint uniView = glGetUniformLocation(program, "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
glm::mat4 proj = glm::perspective(45.0f, 800.0f / 600.0f, 1.0f, 10.0f);
GLint uniProj = glGetUniformLocation(program, "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
and the shader:
layout (location = 0) in vec3 position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
void main() {
gl_Position = proj * view * model * vec4(position, 1.0);
}
I think i did something wrong with setting up the uniforms, because it doesn't draw anything, even if i set model, view and proj to the identity. Could be i just mistyped something, but i really can't find the problem.
Edit: Solved it, the problem was that i forgot to use glUseProgram() first.
The first thing to do is check all possible return codes, verify your shader program has compiled and linked correctly, and make sure your uniforms are valid, which means the location value is >= 0.
A binary search with glGetError() can also be very useful in these instances when you don't know where it's going wrong.
After compiling the shaders, make sure you check glGetProgram(GL_LINK_STATUS,...). And finally, you must call glUseProgram() to activate the shader.
As #Vallentin suggests, it is much more efficient to pass in your precalculated MVP matrix, since it will not change between the app and the shader. This simplifies your code somewhat, to something like this for your application code:
// Set up MVP matrices
glm::mat4 model = glm::mat4();
glm::mat4 view = glm::lookAt(
glm::vec3(2.5f, 2.5f, 2.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 proj = glm::perspective(45.0f, 800.0f / 600.0f, 1.0f, 10.0f);
glm::mat4 mvp = proj * view * model;
glUseProgram(prog);
GLint uniMvp = glGetUniformLocation(program, "mvp");
glUniformMatrix4fv(uniMvp, 1, GL_FALSE, glm::value_ptr(mvp));
and then the GLSL:
// Shader code
layout (location = 0) in vec3 position;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(position, 1.0);
}
Also, you could cache the uniform locations, since they won't change once compiled. This will save a small amount per frame, rather than querying them for every redraw.