c++ - 现代 OpenGL 立方体旋转无法按预期工作

标签 c++ opengl math matrix rotation

我试图围绕一个轴旋转一个立方体,但它的行为肯定不正确。我假设问题出在我的矩阵旋转代码中,因为其他一切似乎都在工作。我可以沿 x、y 或 z 轴以及比例正确平移模型。我的相机 View 矩阵也按预期工作,我的投影矩阵也是如此。如果我删除 View 矩阵和/或投影矩阵实现,问题仍然存在。

如果您想查看我得到的结果,它与此 stackoverflow 帖子中显示的 gif 输出完全相同:Rotating a cube in modern opengl... looks strange 立方体在旋转时似乎会自行折叠,然后在完全旋转后恢复正常,并且似乎可以很好地旋转约 20 度,直到再次折叠并重复。我的问题与文章链接中的问题相同,但是我的矩阵类不一样,所以我的问题虽然相同,但似乎有不同的解决方案。

这是我的带有可能相关运算符的剥离矩阵声明

数学.h

typedef struct matrix4x4
{
    //Elements stored in ROW MAJOR ORDER
    GLfloat matrix[16];

    void translate(Vector3f translation);
    void rotateX(GLfloat angle);
    void rotateY(GLfloat angle);
    void rotateZ(GLfloat angle);
    void rotate(Vector3f angles);
    void scale(Vector3f scales);
    void scale(GLfloat scale);

    inline matrix4x4& operator*=(const matrix4x4& rhs)
    {
        this->matrix[0] = this->matrix[0] * rhs.matrix[0] + this->matrix[1] * rhs.matrix[4] + this->matrix[2] * rhs.matrix[8] + this->matrix[3] * rhs.matrix[12];
        this->matrix[1] = this->matrix[0] * rhs.matrix[1] + this->matrix[1] * rhs.matrix[5] + this->matrix[2] * rhs.matrix[9] + this->matrix[3] * rhs.matrix[13];
        this->matrix[2] = this->matrix[0] * rhs.matrix[2] + this->matrix[1] * rhs.matrix[6] + this->matrix[2] * rhs.matrix[10] + this->matrix[3] * rhs.matrix[14];
        this->matrix[3] = this->matrix[0] * rhs.matrix[3] + this->matrix[1] * rhs.matrix[7] + this->matrix[2] * rhs.matrix[11] + this->matrix[3] * rhs.matrix[15];

        this->matrix[4] = this->matrix[4] * rhs.matrix[0] + this->matrix[5] * rhs.matrix[4] + this->matrix[6] * rhs.matrix[8] + this->matrix[7] * rhs.matrix[12];
        this->matrix[5] = this->matrix[4] * rhs.matrix[1] + this->matrix[5] * rhs.matrix[5] + this->matrix[6] * rhs.matrix[9] + this->matrix[7] * rhs.matrix[13];
        this->matrix[6] = this->matrix[4] * rhs.matrix[2] + this->matrix[5] * rhs.matrix[6] + this->matrix[6] * rhs.matrix[10] + this->matrix[7] * rhs.matrix[14];
        this->matrix[7] = this->matrix[4] * rhs.matrix[3] + this->matrix[5] * rhs.matrix[7] + this->matrix[6] * rhs.matrix[11] + this->matrix[7] * rhs.matrix[15];

        this->matrix[8] = this->matrix[8] * rhs.matrix[0] + this->matrix[9] * rhs.matrix[4] + this->matrix[10] * rhs.matrix[8] + this->matrix[11] * rhs.matrix[12];
        this->matrix[9] = this->matrix[8] * rhs.matrix[1] + this->matrix[9] * rhs.matrix[5] + this->matrix[10] * rhs.matrix[9] + this->matrix[11] * rhs.matrix[13];
        this->matrix[10] = this->matrix[8] * rhs.matrix[2] + this->matrix[9] * rhs.matrix[6] + this->matrix[10] * rhs.matrix[10] + this->matrix[11] * rhs.matrix[14];
        this->matrix[11] = this->matrix[8] * rhs.matrix[3] + this->matrix[9] * rhs.matrix[7] + this->matrix[10] * rhs.matrix[11] + this->matrix[11] * rhs.matrix[15];

        this->matrix[12] = this->matrix[12] * rhs.matrix[0] + this->matrix[13] * rhs.matrix[4] + this->matrix[14] * rhs.matrix[8] + this->matrix[15] * rhs.matrix[12];
        this->matrix[13] = this->matrix[12] * rhs.matrix[1] + this->matrix[13] * rhs.matrix[5] + this->matrix[14] * rhs.matrix[9] + this->matrix[15] * rhs.matrix[13];
        this->matrix[14] = this->matrix[12] * rhs.matrix[2] + this->matrix[13] * rhs.matrix[6] + this->matrix[14] * rhs.matrix[10] + this->matrix[15] * rhs.matrix[14];
        this->matrix[15] = this->matrix[12] * rhs.matrix[3] + this->matrix[13] * rhs.matrix[7] + this->matrix[14] * rhs.matrix[11] + this->matrix[15] * rhs.matrix[15];
        return *this;
    }

}matrix4x4;

matrix4x4 createTransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale);
matrix4x4 createPerspectiveProjectionMatrix(GLfloat width, GLfloat height, GLfloat fov, GLfloat nearPlane, GLfloat farPlane);
matrix4x4 createViewMatrix(Vector3f cameraPosition, GLfloat cameraPitch, GLfloat cameraYaw, GLfloat cameraRoll);

及其相关实现 数学.cpp

matrix4x4::matrix4x4(GLfloat elements[])
{
    //Elements stored in ROW MAJOR ORDER
    for (unsigned int i = 0; i <= elementCount; i++)
    {
        matrix[i] = elements[i];
    }
}

void matrix4x4::setIdentity()
{
    std::fill(matrix, matrix + sizeof(matrix) / sizeof(GLfloat), 0.0f);
    matrix[0] = 1;
    matrix[5] = 1;
    matrix[10] = 1;
    matrix[15] = 1;
}

/*/////////////////////////////////////////////////////
    math
/////////////////////////////////////////////////////*/

void matrix4x4::translate(Vector3f translation)
{
    GLfloat transformElements[16] =
    {
        1.0f, 0.0f, 0.0f, translation.x,
        0.0f, 1.0f, 0.0f, translation.y,
        0.0f, 0.0f, 1.0f, translation.z,
        0.0f, 0.0f, 0.0f, 1.0f
    };
    matrix4x4 transform = matrix4x4(transformElements);

    *this *= transform;
}

void matrix4x4::rotateX(GLfloat angle)
{
    angle = degreesToRadians(angle);

    GLfloat transformElements[16] =
    {
        1.0f,       0.0f,               0.0f,           0.0f,
        0.0f,   std::cos(-angle),   -std::sin(-angle),  0.0f,
        0.0f,   std::sin(-angle),   std::cos(-angle),   0.0f,
        0.0f,       0.0f,               0.0f,           1.0f
    };
    matrix4x4 transform = matrix4x4(transformElements);

    *this *= transform;
}

void matrix4x4::rotateY(GLfloat angle)
{
    angle = degreesToRadians(angle);

    GLfloat transformElements[16] =
    {
        std::cos(-angle),   0.0f,   std::sin(-angle),   0.0f,
        0.0f,               1.0f,       0.0f,           0.0f,
        -std::sin(-angle),  0.0f,   std::cos(-angle),   0.0f,
        0.0f,               0.0f,       0.0f,           1.0f
    };
    matrix4x4 transform = matrix4x4(transformElements);

    *this *= transform;
}

void matrix4x4::rotateZ(GLfloat angle)
{
    angle = degreesToRadians(angle);

    GLfloat transformElements[16] =
    {
        std::cos(-angle),   -std::sin(-angle),  0.0f,   0.0f,
        std::sin(-angle),   std::cos(-angle),   0.0f,   0.0f,
        0.0f,                   0.0f,           1.0f,   0.0f,
        0.0f,                   0.0f,           0.0f,   1.0f
    };
    matrix4x4 transform = matrix4x4(transformElements);

    *this *= transform;
}

void matrix4x4::rotate(Vector3f angles)
{
    matrix4x4 transform = matrix4x4();
    transform.setIdentity();

    transform.rotateX(angles.x);
    transform.rotateY(angles.y);
    transform.rotateZ(angles.z);

    *this *= transform;
}

void matrix4x4::scale(Vector3f scales)
{
    GLfloat transformElements[16] =
    {
        scales.x,   0.0f,       0.0f,       0.0f,
        0.0f,       scales.y,   0.0f,       0.0f,
        0.0f,       0.0f,       scales.z,   0.0f,
        0.0f,       0.0f,       0.0f,       1.0f
    };
    matrix4x4 transform = matrix4x4(transformElements);

    *this *= transform;
}

matrix4x4 createTransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale)
{
    matrix4x4 transformationMatrix;
    transformationMatrix.setIdentity();

    //I've tried changing the order of these around, as well as only
    //doing one operation (skipping translate and scale, or everything but a single axis rotation
    transformationMatrix.translate(translation);
    transformationMatrix.rotate(rotation);
    transformationMatrix.scale(scale);

    return transformationMatrix;
}

matrix4x4 createPerspectiveProjectionMatrix(GLfloat width, GLfloat height, GLfloat fov, GLfloat nearPlane, GLfloat farPlane)
{
    matrix4x4 projectionMatrix;
    projectionMatrix.setIdentity();

    GLfloat aspectRatio = width / height;

    projectionMatrix.matrix[0] = (1.0f / std::tan((degreesToRadians(fov)) / 2.0f) / aspectRatio);
    projectionMatrix.matrix[5] = 1.0f / std::tan((degreesToRadians(fov)) / 2.0f);
    projectionMatrix.matrix[10] = (farPlane + nearPlane) / (nearPlane - farPlane);
    projectionMatrix.matrix[11] = (2.0f * farPlane * nearPlane) / (nearPlane - farPlane);
    projectionMatrix.matrix[14] = -1.0f;

    return projectionMatrix;
}

我知道我的矩阵/vector 实现既快速又肮脏,但我只是想设置一些东西。我有计划使数学方法(缩放、翻译等)成为不影响矩阵内容的静态方法,而是接受一个矩阵作为输入并返回一个新的……但这不是问题现在。

这是我的顶点着色器

#version 330 core

//declare inputs
in vec3 position;
in vec2 textureCoords;

//declare output
out vec2 pass_textureCoords;

//uniforms
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;

void main(void)
{
    //tell OpenGL where to render the vertex on screen
    gl_Position = projectionMatrix * viewMatrix * transformationMatrix * vec4(position.x, position.y, position.z, 1.0);

    pass_textureCoords = textureCoords;
}

我的渲染方法...

void Renderer::render(Entity entity, Shader* shader)
{
    ...

    RawModel* rawModel = texturedModel->getRawModel();

    glBindVertexArray(rawModel->getVaoID());
    ...

    matrix4x4 transformationMatrix = createTransformationMatrix(entity.getPosition(), entity.getRotation(), entity.getScale());
    shader->loadTransformationMatrix(transformationMatrix);

    ...

    glDrawElements(GL_TRIANGLES, rawModel->getVertexCount(), GL_UNSIGNED_INT, 0);
    ...
}

最后是我主要的相关作品。立方体定义等

//This is a simple cube
    std::vector<GLfloat> vertices = 
    {
        -0.5f,0.5f,-0.5f,
        -0.5f,-0.5f,-0.5f,
        0.5f,-0.5f,-0.5f,
        0.5f,0.5f,-0.5f,

        -0.5f,0.5f,0.5f,
        -0.5f,-0.5f,0.5f,
        0.5f,-0.5f,0.5f,
        0.5f,0.5f,0.5f,

        0.5f,0.5f,-0.5f,
        0.5f,-0.5f,-0.5f,
        0.5f,-0.5f,0.5f,
        0.5f,0.5f,0.5f,

        -0.5f,0.5f,-0.5f,
        -0.5f,-0.5f,-0.5f,
        -0.5f,-0.5f,0.5f,
        -0.5f,0.5f,0.5f,

        -0.5f,0.5f,0.5f,
        -0.5f,0.5f,-0.5f,
        0.5f,0.5f,-0.5f,
        0.5f,0.5f,0.5f,

        -0.5f,-0.5f,0.5f,
        -0.5f,-0.5f,-0.5f,
        0.5f,-0.5f,-0.5f,
        0.5f,-0.5f,0.5f
    };
    std::vector<GLfloat> textureCoords =
    {
        ...
    };
    std::vector<GLuint> indices = 
    {
        0,1,3,
        3,1,2,
        4,5,7,
        7,5,6,
        8,9,11,
        11,9,10,
        12,13,15,
        15,13,14,
        16,17,19,
        19,17,18,
        20,21,23,
        23,21,22
    };

    //parameters are (model, pos, rotation, scale)
    Entity entity = Entity(&texturedModel, Vector3f(0.0f, 0.0f, -2.0f), Vector3f(0.0f, 0.0f, 0.0f), 1.0f);

    //SHADER STUFF
    Shader textureShader = Shader("uniformVarTextureShader");
    textureShader.loadProjectionMatrix(display.getProjectionMatrix());

    Camera cam;

    //draw in wireframe mode
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    while (display.checkForClose() == 0)
    {
        glfwPollEvents();

        //TO DO: update logic here
        //entity.varyPosition(+0.005f, 0.0f, -0.002f); //this works, as does scaling and camera movement
        //entity.varyRotation(0.25f, 0.18f, 0.0f);
        entity.setYRotation(entity.getYRotation() + 0.25f); //any sort of rotation operation ends up with the strange behaivor

        //rendering commands here
        display.prepare();

        textureShader.bind();
        textureShader.loadViewMatrix(cam);
        display.render(entity, &textureShader);
        textureShader.stop();

        display.swapBuffers();
    }

所以,回顾一下;我在翻译、缩放、“相机移动”方面没有任何问题,投影矩阵似乎也能正常工作。但是,每当我尝试旋转时,我都会得到与上面文章链接完全相同的行为。

最后说明:我启用了深度测试并清除了每一帧的深度缓冲区。我还传递 GL_TRUE 以转置我提供给 glUniformMatrix4fv 的任何矩阵数据。我已经检查了每件制服的位置,它们都正确地通过了;分别为 0、1 和 2。否-1。

我很困惑,如有任何帮助,我们将不胜感激。如果需要,我可以发布更多代码,但我很确定这涵盖了问题最有可能出现的全部地方。再次感谢

最佳答案

主要问题是矩阵乘法操作。 由于您操纵矩阵(从矩阵读取并写入矩阵),在您读取矩阵之前是否已经操纵了一些元素。

例如在第一行 this->matrix[0] 被写入

this->matrix[0] = this->matrix[0] * rhs.matrix[0] + this->matrix[1] * rhs.matrix[4] + this->matrix[2] * rhs.matrix[8] + this->matrix[3] * rhs.matrix[12];

在第二行中再次读取this->matrix[0]:

this->matrix[1] = this->matrix[0] * rhs.matrix[1] + this->matrix[1] * rhs.matrix[5] + this->matrix[2] * rhs.matrix[9] + this->matrix[3] * rhs.matrix[13];

复制矩阵数组到局部变量,解决问题:

matrix4x4& operator*=(const matrix4x4& rhs)
{
    matrix4x4 act( this->matrix );

    this->matrix[0] = act.matrix[0] * rhs.matrix[0] + act.matrix[1] * rhs.matrix[4] + act.matrix[2] * rhs.matrix[8] + act.matrix[3] * rhs.matrix[12];
    this->matrix[1] = act.matrix[0] * rhs.matrix[1] + act.matrix[1] * rhs.matrix[5] + act.matrix[2] * rhs.matrix[9] + act.matrix[3] * rhs.matrix[13];

    ....

    return *this;
}



顺便说一句,因为你在着色器中从右边乘以一个 vector 到矩阵

gl_Position = projectionMatrix * viewMatrix * transformationMatrix * vec4(position.x, position.y, position.z, 1.0);

矩阵必须按列主要顺序初始化:

mat4 m44 = mat4(
    vec4( Xx, Xy, Xz, 0.0),
    vec4( Yx, Xy, Yz, 0.0),
    vec4( Zx  Zy  Zz, 0.0),
    vec4( Tx, Ty, Tz, 1.0) );

请注意,您的矩阵是按行主要顺序初始化的,例如matrix4x4::translate:

GLfloat transformElements[16] =
{
    1.0f, 0.0f, 0.0f, translation.x,
    0.0f, 1.0f, 0.0f, translation.y,
    0.0f, 0.0f, 1.0f, translation.z,
    0.0f, 0.0f, 0.0f, 1.0f
};

所以当你将它设置为uniform glUniformMatrix4fv时你必须转置矩阵:

glUniformMatrix4fv( ..., ..., GL_TRUE, ... );

关于c++ - 现代 OpenGL 立方体旋转无法按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49889920/

相关文章:

c++ - 如何在 OpenCV 中以编程方式在 YAML 中编写注释?

c++ - 使类不可复制*和*不可 move

android - 如何在 native Android 应用程序中获取显示指标

c++ - 使用 Freetype 和 OpenGL 模糊文本

r - 在 R-Server Ubuntu 上安装 rgl

algorithm - 在一个方向上与原点相交 minkowski 差异,我如何找到相交的脸?

java - Math.random() 和精度损失的好奇心

c++ - 将数据保存到dll

c++ - Qt OpenGL 磅值

java - 在Java中,如何对总金额的一定金额应用利息