OpenGL4: Rotation looks all wrong

219 views Asked by At

Here's the vertex buffer information of the quad I'm drawing:

static const GLfloat pv_quad[] = {
    -1.0f, -1.0f, 0.0f,
     1.0f, -1.0f, 0.0f,
    -1.0f,  1.0f, 0.0f,
     1.0f,  1.0f, 0.0f,
};

This quad is used to draw 2D frames on the screen as part of the graphical user interface. The class I use to do this is Mage::Interface::Frame. I'll spare you the header definition and instead give you the class's implementation, as it's small. There's some test code in here, so ignore the fact the shader is part of the class. I know it shouldn't be there.

#include <Mage/Root.h>
#include <Mage/Interface/Frame.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>

using Mage::Interface::Frame;

Frame::Frame()  
: width(300), height(200), position(0, 0), color(1.0, 1.0, 1.0), model(1.0), rotation(0) {
    prog.compileFile("Data/Shaders/FrameVertex.glsl", Mage::ShaderType::VERTEX);
    prog.compileFile("Data/Shaders/FrameFragment.glsl", Mage::ShaderType::FRAGMENT);
    prog.link();

    this->calcTransform();
}

void Frame::setSize(int w, int h) {
    this->width = w;
    this->height = h;
    this->calcTransform();
}

void Frame::setColor(int r, int g, int b) {
    this->color = glm::vec3(float(r) / 256, float(g) / 256, float(b) / 256);
}

void Frame::setRotation(float degrees) {
    this->rotation = glm::radians(degrees);
    this->calcTransform();
}

void Frame::calcTransform() {
    this->model = glm::mat4(1.0f); // reset model to origin.
    // 1280 and 720 are the viewport's size. This is only hard coded for tests.
    this->model = glm::scale(this->model, glm::vec3(float(width) / 1280, float(height) / 720, 1.0f));
    this->model = glm::rotate(this->model, this->rotation, glm::vec3(0.0f, 0.0f, 1.0f));
    this->model = glm::translate(this->model, glm::vec3(position.x, position.y, 0.0f));
}

void Frame::draw() {
    Mage::VertexObject obj = ROOT.getRenderWindow()->getVertexBufferObject()->getObject("PrimitiveQuad");

    prog.use();
    prog.setUniform("mvp", this->model);
    prog.setUniform("fColor", this->color);

    glEnableVertexAttribArray(0);
    ROOT.getRenderWindow()->getVertexBufferObject()->bind();
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)obj.begin);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, obj.size);
    glDisableVertexAttribArray(0);
}

Here's the drawing function that's called every frame:

void RenderWindow::render() {
    Mage::Interface::Frame F;

    F.setSize(400, 200);
    F.setRotation(0);

    while (glfwWindowShouldClose(this->win) == 0) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        F.draw();

        glfwSwapBuffers(this->win);
        glfwPollEvents();
    }
}

When I have setRotation(0), the resulting quad is indeed, 400 pixels wide and 200 pixels high, right in the centre of my screen as you would expect.

However, if I set the rotation to (90), well, this happens:

enter image description here

As you can see, that's not at all close to a 90 degrees turn. It should be 400px high and 200px wide.

Anyone care to explain what's going on here?

EDIT: Some playing around has shown me that the problem is with the scale, not the rotation. When I comment out the scale, the rotation appears to be correct.

2

There are 2 answers

1
genpfault On BEST ANSWER

The angle argument to glm::rotate() is in radians, not degrees:

m: Input matrix multiplied by this rotation matrix.

angle: Rotation angle expressed in radians.

axis: Rotation axis, recommanded [sic] to be normalized.

Use this:

void Frame::setRotation(float degrees) {
    this->rotation = glm::radians( degrees );
    this->calcTransform();
}
1
NickM13 On

I am assuming that this game is supposed to be a 3D game with a 2D GUI, although this was not specified in the question, though not entirely necessary, as my answer will be the same.

When rendering with a 3D matrix, using a perspective view (Field of View taken into account), as opposed to using an orthographic view, the shapes will bend to their position depending on the fov.

So with that, I propose that you use a simple solution, and initialize a 2D viewing matrix (or orthographic matrix) for your 2D interface. If you are just looking for a simple way to render a 2D quad onto the screen freeGLUT(free Graphics Library Utility Toolkit) is there for you. There are plenty of docs out there to help install freeglut, so once you finish that, initialize a 2D rendering matrix, then render the quad using glVertex2i/f or glVertex3i/f, like so:

void setView2d()
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, *SCREEN_WIDTH, *SCREEN_HEIGHT, 0);
    glMatrixMode( GL_MODELVIEW );
    glDisable(GL_DEPTH_TEST);
    glLoadIdentity();
}

void setView3d()
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(70, (GL_FLOAT)*SCREEN_WIDTH / *SCREEN_HEIGHT, 0.1, 100);
    glEnable(GL_DEPTH_TEST);
    glLoadIdentity();
}

void render()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_TEST);

    setView2d(); //Render 2D objects
    glPushMatrix();
    {
        //glTranslatef() and glRotatef() still work for 2D
        //if using rotate, rotate on z axis, like so:
        glRotatef(90, 0, 0, 1);
        glBegin(GL_TRIANGLES);
        {
            glVertex2i(0, 0);
            glVertex2i(100, 0);
            glVertex2i(0, 100);

            /*
                glVertex2i is replacable with glVertex2f, glVertex3i, and glVertex3f
                if using a glVertex3, set the z value to 0
            */
        }
        glEnd();
    }
    glPopMatrix();


    setView3d(); //Render 3D objects
    glPushMatrix();
    {
        //render 3D stuff
    }
    glPopMatrix();

    glutSwapBuffers();
}

I should also mention that when using the gluOrtho2D, coordinates used in vertex x,y are based on pixels, instead of the 3D blocks.

Hope this helped,

-Nick