0
\$\begingroup\$

I have created a 3x3 matrix class which is then passed over to OpenGL Vertex Shader for transform my Quad. I have been trying to rotate my Quad around Z-Axis. Rotation is working but the Quad getting further away from the screen. Below are my Matrix and Vertex Shader source:

Matrix3x3.cpp

#include "matrix3x3.h"
#include "vector2d.h"
#include <cmath>

Matrix3x3::Matrix3x3()
{
    matrix[0][0] = 1.0f;
    matrix[0][1] = 0.0f;
    matrix[0][2] = 0.0f;

    matrix[1][0] = 0.0f;
    matrix[1][1] = 1.0f;
    matrix[1][2] = 0.0f;

    matrix[2][0] = 0.0f;
    matrix[2][1] = 0.0f;
    matrix[2][2] = 1.0f;    
}

void Matrix3x3::setIdentity()
{
    matrix[0][0] = 1.0f;
    matrix[0][1] = 0.0f;
    matrix[0][2] = 0.0f;

    matrix[1][0] = 0.0f;
    matrix[1][1] = 1.0f;
    matrix[1][2] = 0.0f;

    matrix[2][0] = 0.0f;
    matrix[2][1] = 0.0f;
    matrix[2][2] = 1.0f;
}

void Matrix3x3::translate(float tx, float ty, float tz)
{
    float tmpMatrix[3][3];

    tmpMatrix[0][0] = 1.0f;
    tmpMatrix[0][1] = 0.0f;
    tmpMatrix[0][2] = 0.0f;

    tmpMatrix[1][0] = 0.0f;
    tmpMatrix[1][1] = 1.0f;
    tmpMatrix[1][2] = 0.0f;

    tmpMatrix[2][0] = tx;
    tmpMatrix[2][1] = ty;
    tmpMatrix[2][2] = tz;

    mutliply(tmpMatrix);
}

void Matrix3x3::rotate(float angle)
{
    float tmpMatrix[3][3];

    const float radian = 3.141593f / 180 ;//(angle * 3.141592653589793 )/ 180.0;// (2 * PI) / (360.0 / cos(angle));

    float cs = cos(radian * angle);
    float sn = sin(radian * angle);

    tmpMatrix[0][0] = cs;
    tmpMatrix[0][1] = sn;
    tmpMatrix[0][2] = 0.0f;

    tmpMatrix[1][0] = -sn;
    tmpMatrix[1][1] = cs;
    tmpMatrix[1][2] = 0.0f;

    tmpMatrix[2][0] = 0.0f;
    tmpMatrix[2][1] = 0.0f;
    tmpMatrix[2][2] = 1.0f;

    mutliply(tmpMatrix);
}

void Matrix3x3::scale(float sx,float sy)
{
    float tmpMatrix[3][3];

    tmpMatrix[0][0] = sx;
    tmpMatrix[0][1] = 0.0f;
    tmpMatrix[0][2] = 0.0f;

    tmpMatrix[1][0] = 0.0f;
    tmpMatrix[1][1] = sy;
    tmpMatrix[1][2] = 0.0f;

    tmpMatrix[2][0] = 0.0f;
    tmpMatrix[2][1] = 0.0f;
    tmpMatrix[2][2] = 1.0f;

    mutliply(tmpMatrix);
}

void Matrix3x3::transform(vector2d& vec)
{
    /*float tx = vec.x * matrix[0][0] + vec.y * matrix[3] + vec.w * matrix[6];
    float ty = vec.x * matrix[1] + vec.y * matrix[4] + vec.w * matrix[7];
    float tw = vec.x * matrix[2] + vec.y * matrix[5] + vec.w * matrix[8];

    vec.x = tx;
    vec.y = ty;
    vec.w = tw;*/
}

void Matrix3x3::mutliply(const float(&matrixb)[3][3])
{
    /*matrix[0] = (matrix[0] * matrixb[0]) + (matrix[1] * matrixb[3]) + (matrix[2] * matrixb[6]); 
    matrix[1] = (matrix[0] * matrixb[1]) + (matrix[1] * matrixb[4]) + (matrix[2] * matrixb[7]);
    matrix[2] = (matrix[0] * matrixb[2]) + (matrix[1] * matrixb[5]) + (matrix[2] * matrixb[8]);

    matrix[3] = (matrix[3] * matrixb[0]) + (matrix[4] * matrixb[3]) + (matrix[5] * matrixb[6]);
    matrix[4] = (matrix[3] * matrixb[1]) + (matrix[4] * matrixb[4]) + (matrix[5] * matrixb[7]);
    matrix[5] = (matrix[3] * matrixb[2]) + (matrix[4] * matrixb[5]) + (matrix[5] * matrixb[8]);

    matrix[6] = (matrix[6] * matrixb[0]) + (matrix[7] * matrixb[3]) + (matrix[8] * matrixb[6]);
    matrix[7] = (matrix[6] * matrixb[1]) + (matrix[7] * matrixb[4]) + (matrix[8] * matrixb[7]);
    matrix[8] = (matrix[6] * matrixb[2]) + (matrix[7] * matrixb[5]) + (matrix[8] * matrixb[8]);*/

    for (int x = 0; x<3; ++x)
        for (int y = 0; y<3; ++y)
        {
            float sum = 0;
            for (int z = 0; z<3; ++z)
                sum += matrix[x][z] * matrixb[z][y];

            matrix[x][y] = sum;
        }
}

ostream& operator<<(ostream& os,Matrix3x3& matrix)
{
    os << matrix.matrix[0][0] << " " << matrix.matrix[0][1] << " " << matrix.matrix[0][2] << std::endl;
    os << matrix.matrix[1][0] << " " << matrix.matrix[1][1] << " " << matrix.matrix[1][2] << std::endl;
    os << matrix.matrix[2][0] << " " << matrix.matrix[2][1] << " " << matrix.matrix[2][2] << std::endl;

    return os;
}


float* Matrix3x3::getPtr()
{
    return &matrix[0][0];
}

vertex_shader.glsl

#version 330 core

layout (location = 0) in vec3 position;

uniform mat3 transform;

void main()
{
    gl_Position = vec4(transform  * vec3(position.x,position.y,0.0) , 1.0);
}

This is where i am using my matrix in the Quad class:

void Quad::rotate(float angle2)
{

    glUseProgram(shader->getProgramId());   
    this->transformMatrix->rotate(angle);   

    shader->setUniformMatrix3("transform", this->transformMatrix->getPtr());        
}

I have also tried to debug my GLSL shader to check the gl_Position . But its shows that my Z-Axis is always zero and W component is always 1. No idea why its getting away from my screen.

\$\endgroup\$
  • \$\begingroup\$ probably some accumulative rounding errors. check the values of the matrix and ensure that the length of the collums are consistent and the first two are orthogonal \$\endgroup\$ – ratchet freak Jan 6 '16 at 11:07
2
\$\begingroup\$

The quad is getting further away due to rounding errors in the cumulative matrix operations (it's actually getting scaled down).

You must either normalize the matrix or use a rotation angle variable and recreate the rotation matrix from that variable on every frame.

For the translation you must use vec3(position.x, position.y, 1.0) so that your last row becomes the translation.

Setting it to 0.0 multiplies the whole matrix row by 0.0 which nullifies your translation.

Because

transform  * vec3(position.x, position.y, 0.0)

is the same as

transform[0] * position.x + transform[1] * position.y + transform[2] * 0.0
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the Answer that makes sense. Now i have changed it into the create rotation matrix each frame. Scaling is fixed , but now i am facing another issue, my quad rotates around z-axis for some angle but after a while its started to rotate around other axis also. \$\endgroup\$ – aaa111 Jan 7 '16 at 5:56
  • \$\begingroup\$ Make sure to wrap around the rotation values between 0 and full circle (2*pi if it's in radians) this is another case of rounding issues with floating points. \$\endgroup\$ – Stephane Hockenhull Jan 7 '16 at 13:29
  • \$\begingroup\$ Can you elaborate a little more please. \$\endgroup\$ – aaa111 Jan 7 '16 at 13:55
  • \$\begingroup\$ sin/cos and other functions can get less and less precise as the numbers get larger. It's the nature of floating point numbers that causes this. 2*pi is a full circle so the rotation value can be wrapped-around at that point to keep the angle from getting too large. use current_angle = fmodf(current_angle + change, 2*M_PI); every time you change the rotation angle so the value stays between -2*pi and 2*pi. \$\endgroup\$ – Stephane Hockenhull Jan 8 '16 at 6:32
1
\$\begingroup\$

After spending whole day i finally found out whats the issue actually. Its really silly of me that its got overlooked at the very first place. So here is the problem :

matrix[1][1] = (matrix[1][0] * matrixb[0][1]) + (matrix[1][1] * matrixb[1][1]) + (matrix[1][2] * matrixb[2][1]);

Here i am setting matrix[1][1] to a new value. But in the next line i need to multiply the matrix[1][1] which need the old value

 matrix[1][2] = (matrix[1][0] * matrixb[0][2]) + (matrix[1][1] * matrixb[1][2]) + (matrix[1][2] * matrixb[2][2]);

So now what i did is i am multiplying the matrix using temporaries again and copy the values over the root matrix.

void Matrix3x3::mutliply(const float(&matrixb)[3][3])
{
    float tmat[3][3];

    tmat[0][0] = (matrix[0][0] * matrixb[0][0]) + (matrix[0][1] * matrixb[1][0]) + (matrix[0][2] * matrixb[2][0]);
    tmat[0][1] = (matrix[0][0] * matrixb[0][1]) + (matrix[0][1] * matrixb[1][1]) + (matrix[0][2] * matrixb[2][1]);
    tmat[0][2] = (matrix[0][0] * matrixb[0][2]) + (matrix[0][1] * matrixb[1][2]) + (matrix[0][2] * matrixb[2][2]);

    tmat[1][0] = (matrix[1][0] * matrixb[0][0]) + (matrix[1][1] * matrixb[1][0]) + (matrix[1][2] * matrixb[2][0]);
    tmat[1][1] = (matrix[1][0] * matrixb[0][1]) + (matrix[1][1] * matrixb[1][1]) + (matrix[1][2] * matrixb[2][1]);
    tmat[1][2] = (matrix[1][0] * matrixb[0][2]) + (matrix[1][1] * matrixb[1][2]) + (matrix[1][2] * matrixb[2][2]);

    tmat[2][0] = (matrix[2][0] * matrixb[0][0]) + (matrix[2][1] * matrixb[1][0]) + (matrix[2][2] * matrixb[2][0]);
    tmat[2][1] = (matrix[2][0] * matrixb[0][1]) + (matrix[2][1] * matrixb[1][1]) + (matrix[2][2] * matrixb[2][1]);
    tmat[2][2] = (matrix[2][0] * matrixb[0][2]) + (matrix[2][1] * matrixb[1][2]) + (matrix[2][2] * matrixb[2][2]);

    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 3; ++j)
            matrix[i][j] = tmat[i][j];
    }
}

Now all is working great. Only thing i need to do now is to find a way to optimize my matrix class.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You may want to consider using an existing battle-hardened library like MathGeoLib or GLM once you've sated your desire to handroll them. Finding subtle bugs in your math operations grows tiresome after a while. If you still insist on having your own, your tests could still use libraries to get ground truth to verify your operations against. \$\endgroup\$ – Lars Viklund Jan 9 '16 at 3:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.