如何解决多个对象的 OpenGL 旋转在绘制循环中被覆盖
我想创建一个魔方立方体,其中每一面都包含多个立方体。到目前为止,我能够创建整个立方体。我现在想将立方体的一侧向左旋转,为此我创建了一个新矩阵,然后将统一体绑定到该矩阵。这工作正常,但如果我进行第二次操作,比如旋转另一边,我会再次创建一个带有旋转的新矩阵,并将制服绑定到该矩阵。在第二次旋转后绘制我的对象后,我的对象将重置为初始矩阵(旋转已恢复)。
左旋转代码:
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,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,glm::value_ptr(anim));
}
}
return anim;
}
glm::mat4 spinObj2(glm::mat4 anim,float orientation) {
float angle = 0.1f * orientation;
anim = glm::translate(anim,-2.1f) );
anim = glm::rotate(anim,glm::vec3(1.0f,0.0f));
anim = glm::translate(anim,-(glm::vec3(0.0f,-2.1f)) );
return anim;
}
负责绘图的代码,vtxArray 是包含我的立方体的数组,它们构成了整个立方体。使用 createAnim 函数,我绑定了一个新矩阵(它只是单位矩阵)。如果我不调用 createAnim,立方体不会重置为初始立方体,但我会遇到所有行都在旋转的问题,而不仅仅是我定义的 1 行。
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,i);
if (nrRotations == 900) {
move++;
nrRotations = 0;
}
}
glBufferData(GL_ARRAY_BUFFER,6*36*4,&vtxArray[i],GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLES,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)) {
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,1,// 1
-0.5,// 0
0.5,0.5,// 2
-0.5,// 0
-0.5,// 3
0.5,// 2
-0.5,// 1
0.5,// 2
0.5,// 3
-0.5,// 0
-0.5,//0
0.5,//1
0.5,//2
0.5,//2
-0.5,//3
-0.5,//0
0.5,//1
-0.5,//0
-0.5,//3
0.5,//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,(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"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 outColor;\n"
"void main()\n"
"{\n"
" FragColor = outColor;\n"
"}\n\0";
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader,&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,&fragmentShaderSource,NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader,&success);
if (!success)
{
glGetShaderInfoLog(fragmentShader,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,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()),&mat[0][0]);
}
void draw() {
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES,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);
}
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);
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,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,GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT,GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT,deltaTime);
}
// glfw: whenever the mouse moves,this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window,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 yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
void mouse_button_callback(GLFWwindow* window,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);
}
}
输出:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。