Search code examples
c++openglmatrixglm-math

Cube doesn't render as expected when rotated


I'm developing an application in C++ using OpenGL. My problem is that when I render a cube to screen which has a rotation, it appears out of proportion (see the images).

I'm using the following NuGet packages:

Nuget Packages

When the cube is rotated at 0 degrees:

cube at 0 degrees

When the cube is rotated at 45 degrees:

cube at 45 degrees

When the cube is rotated at 90 degrees:

cube at 90 degrees

When the cube is rotated at 180 degrees:

cube at 180 degrees

When the cube is rotated at 360 degrees:

cube at 360 degrees isn't even visible

The following code is where the matrix is calculated:

glm::mat4 DrawnEntity::getMatrix() const
{
    glm::mat4 translate = glm::translate(glm::mat4(1.0f), position);
    glm::mat4 rotation = glm::toMat4(glm::quat(glm::radians(360.0f), 0, 1, 0));

    glm::mat4 matrix = translate * rotation;
    return matrix;
}

When the entity is drawn, this is called:

void DrawnEntity::render()
{
    if (enabled)
        mesh->render(getMatrix());
}

Which subsequently calls this:

void Mesh::render(glm::mat4 matrix)
{
    glUseProgram(shaderID);
    vao->render(matrix);
}

vao is a VertexArrayObject, and this is the function:

void VertexArrayObject::render(glm::mat4 matrix)
{
    GLuint uModel = glGetUniformLocation(shaderID, "uModel");
    glUniformMatrix4fv(uModel, 1, GL_TRUE, &matrix[0][0]);
    glBindVertexArray(vao[0]);
    glDrawArrays(GL_TRIANGLES, 0, mesh->vertexCount());
    glBindVertexArray(0);
}

Ignoring the rotation, it appears all the vertices are being correctly loaded. For what it's worth, this is the class which generates the cube:

#include "CubeMesh.h"
#include "../VertexArrayObject.h"

CubeMesh::CubeMesh(GLuint shader)
{
    shaderID = shader;
    glUseProgram(shaderID);
    generateFaces();
    vao = new VertexArrayObject(this);
}

void CubeMesh::generateFaces()
{
    // Front face
    generateFace(glm::vec3(-0.5, 0.5, 0.5), glm::vec3(0.5, -0.5, 0.5));
    // Left face
    generateFace(glm::vec3(-0.5, -0.5, 0.5), glm::vec3(-0.5, 0.5, -0.5));
    // Back face
    generateFace2(glm::vec3(0.5, -0.5, -0.5), glm::vec3(-0.5, 0.5, -0.5));
    // Right face
    generateFace2(glm::vec3(0.5, -0.5, 0.5), glm::vec3(0.5, 0.5, -0.5));
}

void CubeMesh::generateFace(glm::vec3 point1, glm::vec3 point2)
{
    glm::vec3 tl = point1;
    glm::vec3 br = point2;
    glm::vec3 tr = glm::vec3(br.x, tl.y, br.z);
    glm::vec3 bl = glm::vec3(tl.x, br.y, tl.z);

    Vertex f1v1(glm::vec3(tl.x, tl.y, tl.z));
    Vertex f1v2(glm::vec3(bl.x, bl.y, bl.z));
    Vertex f1v3(glm::vec3(br.x, br.y, br.z));
    Triangle f1(f1v1, f1v2, f1v3);

    Vertex f2v1(glm::vec3(tr.x, tr.y, tr.z));
    Vertex f2v2(glm::vec3(tl.x, tl.y, tl.z));
    Vertex f2v3(glm::vec3(br.x, br.y, br.z));
    Triangle f2(f2v1, f2v2, f2v3);

    addData(f1);
    addData(f2);
}

void CubeMesh::generateFace2(glm::vec3 point1, glm::vec3 point2)
{
    glm::vec3 tl = point1;
    glm::vec3 br = point2;
    glm::vec3 tr = glm::vec3(br.x, tl.y, br.z);
    glm::vec3 bl = glm::vec3(tl.x, br.y, tl.z);

    Vertex f1v1(glm::vec3(tl.x, tl.y, tl.z));
    Vertex f1v2(glm::vec3(br.x, br.y, br.z));
    Vertex f1v3(glm::vec3(bl.x, bl.y, bl.z));
    Triangle f1(f1v1, f1v2, f1v3);

    Vertex f2v1(glm::vec3(tr.x, tr.y, tr.z));
    Vertex f2v2(glm::vec3(br.x, br.y, br.z));
    Vertex f2v3(glm::vec3(tl.x, tl.y, tl.z));
    Triangle f2(f2v1, f2v2, f2v3);

    addData(f1);
    addData(f2);
}

The vertex shader is as follows:

#version 430 core

uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;

in vec3 vPosition;
in vec3 vNormal;

out vec4 oColour;

void main(void)
{   
    oColour = vec4(vPosition, 1);
    gl_Position = vec4(vPosition, 1) * uModel * uView * uProjection;
}

uModel is the matrix representing the individual model translation, rotation etc. uView is the position of the camera, and uProjection is the projection matrix. The first is fed to the shader in the VertexArrayObject shown previously, while the last two are fed to the shader in the camera object as below:

void Camera::initialise()
{
    glUseProgram(shaderID);

    view = glm::translate(glm::mat4(1), position);

    int uView = glGetUniformLocation(shaderID, "uView");
    glUniformMatrix4fv(uView, 1, GL_TRUE, &view[0][0]);

    int uProjection = glGetUniformLocation(shaderID, "uProjection");
    glm::mat4 projection = glm::perspective(1.0, (double)1024 / (double)1024, 0.01, 10.0);
    glUniformMatrix4fv(uProjection, 1, GL_TRUE, &projection[0][0]);
}

The position of the model is 0 0 0 and the position of the camera is 0 0 -5. When the uModel position is changed, the cube is rendered where it is expected, however with the rotation it doesn't act as it should.

Can anybody see anything that I might be doing wrong? Is there anymore code you need to see?


Solution

  • I found that changing the following line:

    glm::mat4 rotation = glm::toMat4(glm::quat(glm::radians(360.0f), 0, 1, 0));
    

    to this:

    glm::mat4 rotation = glm::rotate(translate, (float)glm::radians(0.0), glm::vec3(0.0f, 1.0f, 0.0f));
    

    solves the issue. I can't really offer an explanation for why the first one didn't work, as I don't know enough about Quaternion's to comment on it. If anyone can explain it better, please edit my answer. Regardless, this was the best fix I could find.