多个对象的 OpenGL 旋转在绘制循环中被覆盖

OpenGL Rotation of multiple Objects is overwritten in draw-loop

我想创建一个 rubiks 立方体,其中每一面都包含多个立方体。到目前为止,我能够创建整个多维数据集。我现在想将立方体的一侧向左旋转,为此我创建了一个新矩阵,然后将制服绑定到该矩阵。这工作正常,但如果我做第二个操作,比如旋转另一侧,我再次创建一个新的旋转矩阵并将制服绑定到该矩阵。在我第二次旋转后绘制我的对象后,我的对象被重置为初始矩阵(旋转被恢复)。

左旋代码:

glm::mat4 spinLeft(glm::mat4 anim, GLuint shaderProgram, int i) {
     if(i < 9) {
        // createAnim(shaderProgram, anim);
        if(nrRotations <= 900) {
            anim = spinObj(anim, 1.0);
            // cout << nrRotations << endl; 
            nrRotations += 1;
            glUniformMatrix4fv(uniformAnim, 1, GL_FALSE, glm::value_ptr(anim));

        }
    }
    return anim;
} 

glm::mat4 spinObj(glm::mat4 anim, float orientation) {
    float angle = 0.1f * orientation;

    anim = glm::rotate(anim, glm::radians(angle), glm::vec3(0.0f, 0.0f, 2.0f));
    
    return anim;
}

右旋代码:

glm::mat4 spinRight(glm::mat4 anim, GLuint shaderProgram, int i) {
        if(i == RIGHT || i == TOP_RIGHT || i == BOTTOM_RIGHT  
            || i == RIGHT+9 || i == TOP_RIGHT+9 || i == BOTTOM_RIGHT+9  
            || i == RIGHT+18 || i == TOP_RIGHT+18 || i == BOTTOM_RIGHT+18)
        {
            // createAnim(shaderProgram, anim);
            if(nrRotations <= 900) {
                anim = spinObj2(anim, 1.0);
                // cout << nrRotations << endl; 
                nrRotations +=1; 
            glUniformMatrix4fv(uniformAnim, 1, GL_FALSE, glm::value_ptr(anim));

            }
        } 

    return anim;
}

glm::mat4 spinObj2(glm::mat4 anim, float orientation) {
    float angle = 0.1f * orientation;

    anim = glm::translate(anim, glm::vec3(0.0f, 0.0f, -2.1f) );
    anim = glm::rotate(anim, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
    anim = glm::translate(anim, -(glm::vec3(0.0f, 0.0f, -2.1f)) );


    return anim;
}

负责绘图的代码,vtxArray 是包含我的立方体的数组,这些立方体构成了整个立方体。使用 createAnim 函数,我绑定了一个新矩阵(它只是单位矩阵)。如果我不调用 createAnim,立方体不会重置为初始立方体,但我会遇到所有行都在旋转的问题,而不仅仅是我定义的第一行。

for(int i = 0; i < arraySize; i+=1) {
            createAnim(shaderProgram, anim);

            if(move == 1) {
                if(i < 9) {
                    anim2 = spinLeft(anim2, shaderProgram, i);
                    if(nrRotations == 900) {
                        move ++;
                        nrRotations = 0;
                        // anim = anim2;
                    }
                }
            } else if (move == 2) {
                anim3 = spinRight(anim3,shaderProgram, i);
                if (nrRotations == 900) {
                    move++;
                    nrRotations = 0;
                }
            }
          
            glBufferData(GL_ARRAY_BUFFER, 6*36*4, &vtxArray[i], GL_STATIC_DRAW);  
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

我为此使用了 1 个 VBO 和 1 个 VAO。希望您理解我遇到的问题,如果需要更多代码或解释,请告诉我。

您将希望利用矩阵乘法。网上有很多关于如何使用矩阵乘法按顺序“链接”旋转的好资源,并且是如何将 MVP 矩阵从单独的模型、视图和投影矩阵链接在一起而不丢失过程中的任何信息的直觉的一部分.

简述:

  • 遍历魔方中的所有方块,过滤构成你要旋转的面的方块(检查其位置是否为某个值)
  • 通过矩阵乘法对其模型矩阵应用旋转
  • 更新块和绘制的制服

如果您遇到困难,这里有一个基于 LearnOpenGL 代码的示例,它演示了链式矩阵乘法:

#include <iostream>
#include <vector>
#include <algorithm>

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>

#include <learnopengl/camera.h>

#include <iostream>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>


using std::vector;
using std::cout;
using std::endl;
using glm::mat4;
using glm::vec2;
using glm::vec3;
using glm::vec4;
using glm::perspective;
using glm::radians;
using glm::normalize;

void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// grid dimensions
const unsigned int GRID_WIDTH = 600;
const unsigned int GRID_HEIGHT = 600;

float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;

// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;

mat4 view;
mat4 projection;
float fov = 90.0f;

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

class Block {
public:
    unsigned int shaderProgram;
    unsigned int VBO, VAO;

    mat4 model;

    Block(vec3 pos=vec3(0,0,0)) {

        model = translate(mat4(1.0), pos);
        // scale it down a little bit to see the different blocks
        model = scale(model, vec3(0.99, 0.99, 0.99));

        float vertices[] = {
             0.5, -0.5, -0.5, 0, 0, 1,// 1
            -0.5, -0.5, -0.5, 0, 0, 1,// 0
             0.5,  0.5, -0.5, 0, 0, 1,// 2
            -0.5, -0.5, -0.5, 0, 0, 1,// 0
            -0.5,  0.5, -0.5, 0, 0, 1,// 3
             0.5,  0.5, -0.5, 0, 0, 1,// 2

            -0.5, -0.5,  0.5, 0, 1, 0,// 0
             0.5, -0.5,  0.5, 0, 1, 0,// 1
             0.5,  0.5,  0.5, 0, 1, 0,// 2
             0.5,  0.5,  0.5, 0, 1, 0,// 2
            -0.5,  0.5,  0.5, 0, 1, 0,// 3
            -0.5, -0.5,  0.5, 0, 1, 0,// 0

            -0.5,  0.5,  0.5, 1, 0, 0,
            -0.5,  0.5, -0.5, 1, 0, 0,
            -0.5, -0.5, -0.5, 1, 0, 0,
            -0.5, -0.5, -0.5, 1, 0, 0,
            -0.5, -0.5,  0.5, 1, 0, 0,
            -0.5,  0.5,  0.5, 1, 0, 0,

             0.5,  0.5, -0.5, 1, 0.5, 0, // 1
             0.5,  0.5,  0.5, 1, 0.5, 0, // 0
             0.5, -0.5, -0.5, 1, 0.5, 0, // 2
             0.5,  0.5,  0.5, 1, 0.5, 0, // 0
             0.5, -0.5,  0.5, 1, 0.5, 0, // 3
             0.5, -0.5, -0.5, 1, 0.5, 0, // 2

            -0.5, -0.5, -0.5, 1, 1, 1,//0
             0.5, -0.5, -0.5, 1, 1, 1,//1
             0.5, -0.5,  0.5, 1, 1, 1,//2
             0.5, -0.5,  0.5, 1, 1, 1,//2
            -0.5, -0.5,  0.5, 1, 1, 1,//3
            -0.5, -0.5, -0.5, 1, 1, 1,//0

             0.5,  0.5, -0.5, 1, 1, 0, //1
            -0.5,  0.5, -0.5, 1, 1, 0, //0
             0.5,  0.5,  0.5, 1, 1, 0, //2
            -0.5,  0.5, -0.5, 1, 1, 0, //0
            -0.5,  0.5,  0.5, 1, 1, 0, //3
             0.5,  0.5,  0.5, 1, 1, 0, //2

        };


        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);

        glBindVertexArray(VAO);

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

        // position attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

        // color attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);

        const char *vertexShaderSource = "#version 330 core\n"
            "layout (location = 0) in vec3 aPos;\n"
            "layout (location = 1) in vec3 aCol;\n"
            "uniform mat4 model;\n"
            "uniform mat4 view;\n"
            "uniform mat4 projection;\n"
            "out vec4 outColor;\n"
            "void main()\n"
            "{\n"
            "   gl_Position = projection * view * model * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
            "   outColor = vec4(aCol,1.0f);\n"
            "}[=10=]";
        const char *fragmentShaderSource = "#version 330 core\n"
            "out vec4 FragColor;\n"
            "in vec4 outColor;\n"
            "void main()\n"
            "{\n"
            "   FragColor = outColor;\n"
            "}\n[=10=]";

        // vertex shader
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors
        int success;
        char infoLog[512];
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // fragment shader
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
            cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl;
        }
        // link shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
            cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl;
        }
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    }

    void setMatrix(std::string name, glm::mat4 mat) {
        glUseProgram(shaderProgram);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);
    }

    void draw() {
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }


    vec3 getPosition() {
        return vec3(model[3]);
    }
};

class Cube {
public:
    vector<Block> blocks;
    Cube() {
        for (int x = -1; x <= 1; x++) {
            for (int y = -1; y <= 1; y++) {
                for (int z = -1; z <= 1; z++) {
                    blocks.push_back(Block(vec3(x,y,z)));
                }
            }
        }
    }

    void draw(mat4 view, mat4 projection) {
        for (int i = 0; i < blocks.size(); i++) {
            blocks[i].setMatrix("model", blocks[i].model);
            blocks[i].setMatrix("view", view);
            blocks[i].setMatrix("projection", projection);
            blocks[i].draw();           
        }

    }

    void rotate(std::string value, bool ccw) {
        int index;
        vec3 axis;
        if (value == "x") {
            index = 0;
            axis = vec3(1.0f, 0.0f, 0.0f);
        }
        if (value == "y") {
            index = 1;
            axis = vec3(0.0f, 1.0f, 0.0f);
        }
        for (int i = 0; i < blocks.size(); i++) {
            if (fabs(blocks[i].getPosition()[index] - 1.0f) < 0.1f ) {
                // create rotation matrix
                mat4 rotationMatrix = glm::rotate(mat4(1.0f), radians(ccw ? -90.0f : 90.0f), axis);
                // multiply with current matrix
                blocks[i].model = rotationMatrix * blocks[i].model;
            }
        }
    }

};

Cube *rubiksCube;

int main()
{

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Rubik's Cube", NULL, NULL);
    if (window == NULL)
    {
        cout << "Failed to create GLFW window" << endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetScrollCallback(window, scroll_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        cout << "Failed to initialize GLAD" << endl;
        return -1;
    }

    projection = perspective(radians(fov), (float)SCR_WIDTH/(float)SCR_HEIGHT, 0.1f, 1000.0f);
    glClearColor(0.0, 0.0, 0.0, 1.0);

    rubiksCube = new Cube();

    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
    while (!glfwWindowShouldClose(window))
    {

        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;


        processInput(window);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        rubiksCube->draw(camera.GetViewMatrix(), projection);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();

    delete rubiksCube;

    return 0;
}

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);     

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}


void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
    if (button == GLFW_MOUSE_BUTTON_1 && action == 1) {
        rubiksCube->rotate("x", true);
    }
    if (button == GLFW_MOUSE_BUTTON_3 && action == 1) {
        rubiksCube->rotate("y", true);
    }

    if (button == GLFW_MOUSE_BUTTON_2 && action == 1) {
        rubiksCube->rotate("x", false);
    }
}

输出: