I am new with OpenGL and I'm trying to learn it by writing my own simple engine. I have implemented texture mapping, world, view and perspective matrices so far. However I have noticed a strange behaviour of rotations. I have a mesh class which has a rotation attributes (around x, y and z axes). When I change these attributes, the object's rotation matrix is recomputed and later it is applied in the vertex shader to transform the vertices. When I rotate the mesh this way between 0 - 90 and 270 - 360 degrees, everything looks fine, but if the rotation is in the 90 - 270 degrees, the triangles that are further away from the camera start to get bigger, instead of getting smaller, than the ones that are closer. The triangles that are closer, however, are drawn in front of the triangles that are further away, so the z coordination should be fine. When the rotation continues, everything gets seamlessly back to OK as soon as 270 degrees are reached. Also, when I apply the rotation matrix with a function in my code (not in the shader), everything works fine, the problem only occurs when I apply the rotation matrix in the shader.
I provide a picture and a video:
https://www.youtube.com/watch?v=jJLT4DTEYyQ&feature=youtu.be
My vertex shader is:
#version 330
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texture_coordination;
layout (location = 2) in vec3 normal;
uniform mat4 perspective_matrix;
uniform mat4 world_matrix;
uniform mat4 view_matrix;
out vec2 uv_coordination;
void main()
{
gl_Position = perspective_matrix * view_matrix * world_matrix * vec4(position, 1.0);
uv_coordination = texture_coordination;
}
I make my matrices like this:
void make_rotation_matrix(float degrees_x, float degrees_y, float degrees_z, float matrix[4][4])
{
degrees_x *= PI_DIVIDED_180; // convert to radians
degrees_y *= PI_DIVIDED_180;
degrees_z *= PI_DIVIDED_180;
float sin_x = sin(degrees_x);
float cos_x = cos(degrees_x);
float sin_y = sin(degrees_y);
float cos_y = cos(degrees_y);
float sin_z = sin(degrees_z);
float cos_z = cos(degrees_z);
matrix[0][0] = cos_z * cos_y;
matrix[1][0] = cos_z * sin_y * sin_x - sin_z * cos_x;
matrix[2][0] = cos_z * sin_y * cos_x + sin_z * sin_x;
matrix[3][0] = 0;
matrix[0][1] = sin_z * cos_y;
matrix[1][1] = sin_z * sin_y * sin_x + cos_z * cos_x;
matrix[2][1] = sin_z * sin_y * cos_x - cos_z * sin_x;
matrix[3][1] = 0;
matrix[0][2] = -1 * sin_y;
matrix[1][2] = cos_y * sin_x;
matrix[2][2] = cos_y * cos_y;
matrix[3][2] = 0;
matrix[0][3] = 0;
matrix[1][3] = 0;
matrix[2][3] = 0;
matrix[3][3] = 1;
}
void make_perspective_matrix(float fov_degrees, float near_plane, float far_plane, float matrix[4][4])
{
float aspect_ratio = global_window_width / ((float) global_window_height);
float range = near_plane - far_plane;
float tan_half_fov = tanf(fov_degrees / 2.0 * PI_DIVIDED_180);
matrix[0][0] = 1.0f / (tan_half_fov * aspect_ratio) ; matrix[1][0] = 0.0f; matrix[2][0] = 0.0f; matrix[3][0] = 0.0f;
matrix[0][1] = 0.0f; matrix[1][1] = 1.0f / tan_half_fov; matrix[2][1] = 0.0f; matrix[3][1] = 0.0f;
matrix[0][2] = 0.0f; matrix[1][2] = 0.0f; matrix[2][2] = (-1 * near_plane - far_plane) / (float) range; matrix[3][2] = 1.0;
matrix[0][3] = 0.0f; matrix[1][3] = 0.0f; matrix[2][3] = 2.0f * far_plane * near_plane / (float) range; matrix[3][3] = 0.0f;
}
void multiply_matrices(float matrix_a[4][4], float matrix_b[4][4], float matrix_result[4][4])
{
unsigned int i,j;
for (j = 0; j < 4; j++)
for (i = 0; i < 4; i++)
matrix_result[i][j] =
matrix_b[0][j] * matrix_a[i][0] +
matrix_b[1][j] * matrix_a[i][1] +
matrix_b[2][j] * matrix_a[i][2] +
matrix_b[3][j] * matrix_a[i][3];
}
void mesh_3d::update_transformation_matrix()
{
float helper_matrix[4][4];
multiply_matrices(this->translation_matrix,this->rotation_matrix,helper_matrix);
multiply_matrices(helper_matrix,this->scale_matrix,this->transformation_matrix);
}
And I draw the mesh like this:
void mesh_3d::draw()
{
glUniformMatrix4fv(world_matrix_location,1,GL_TRUE,(const GLfloat *)this->transformation_matrix); // load this model's transformation matrix
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
if (this->texture != NULL)
{
glActiveTexture(GL_TEXTURE0); // set the active texture unit to 0
glBindTexture(GL_TEXTURE_2D,this->texture->get_texture_object());
}
glBindBuffer(GL_ARRAY_BUFFER,this->vbo);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(vertex_3d),0); // position
glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,sizeof(vertex_3d),(const GLvoid*) 12); // texture coordination
glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,sizeof(vertex_3d),(const GLvoid*) 20); // normal
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,this->ibo);
glDrawElements(GL_TRIANGLES,this->triangles.size() * 3,GL_UNSIGNED_INT,0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
The mesh has it's cale and translation matrix set to identity by default of course.
I've been stuck with this for a few days now and I don't know how to move on, I'd be glad if someone could at least point out what might be wrong.
I have printed out the matrix values for 0 and 122 degrees:
0 degrees:
model (world):
1 0 0 0
0 1 0 0
0 0 1 0
0 0 3 1
view:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
projection (perspective):
0.75 0 0 0
0 1 0 0
0 0 1.06186 1
0 0 -0.618557 0
122 degrees:
model (world):
-0.529919 0 -0.848048 0
0 1 0 0
0.848048 0 0.280814 0
0 0 3 1
view: same as before
projection (perspective): same as before