OpenGLTANotes

🕹️OpenGL丨RubikCube类搭建 - 魔方作业(1)

by ERIN.Z, 2022-10-21


开始写图形学大作业了,来记个笔记!😀 rubikcube

新建OpenGL项目

由于固定管线不会写(...),老师也更鼓励写核心管线——所以还是丢掉参考的固定管线框架,从新项目开始吧!

环境是Win10 with VS2022.

配个库

新建空项目,在项目属性/VC++的 包含目录库目录 中加入所需的库文件。这里主要用到的是GLFW库、GLAD库和管理矩阵的GLM库。 include 在项目属性/链接器/输入中加入glfw3.libopengl32.liblinker 别忘了添加glad.c到项目。 因为之后还想贴材质,把stb_image库也加进来:新建一个cpp文件加入:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

再来点轮子

应省尽省,借用下learnopengl的shader类。 快乐的复制黏贴之前,先明确一下需求: 1) 可以用鼠标对魔方整体旋转任意角度 2) 实现用鼠标放大缩小效果 3) 实现层次的转动(比如点击一层则旋转一层),包括水平层的转动和垂直层的转动 4) 可以选择灯光 5) 可以选择 2-6 阶魔方的显示

learnopengl的这个camera类是FPS类型的camera,但是在我们这个项目中的旋转需要使用TrackBall映射,摄像机的位置视角其实是不变的。所以camera类就先不要了—— mouse_callback()scroll_callback()我们之后在下一篇重写~

其他的内容基本一致————

初始化glfw->
创建窗口->
设置callback函数->
开启鼠标监听->
初始化glad->
启用深度检测->
绑定shader->
准备顶点数据、vaovbo->
读取材质->
渲染循环

其中鼠标滚轮的缩放功能可以直接通过glm::perspective转换为投影矩阵:

//IN RENDER LOOP
// pass projection matrix to shader (note that in this case it could change every frame)
glm::mat4 projection = glm::perspective(glm::radians(cameraZoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
ourShader.setMat4("projection", projection);

...
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    cameraZoom -= (float)yoffset;
    if (cameraZoom < 1.0f)    cameraZoom = 1.0f;
    if (cameraZoom > 45.0f)  cameraZoom = 45.0f;
}

手敲一个顶点数据与贴图坐标,我们就拥有了一个中心为(0,0,0),边长为1的cube!(并可以通过滚轮放大缩小) a cube

rubikcube类

魔方更重要的是如何记录其结构,在这里来抄抄固定管线模板给的框架。 三阶魔方可以抽象为由9个layer和27个cube构成的结构: rubikcube 拓展到n阶,我们的魔方应该含有n3个layer和n^3个cube。 为了给自己省点脑子,这里layer中储存的cube数组我改为了二维nn的数组,朝着旋转轴方向看,从左下角开始按顺序索引。 依法炮制一下n阶rubikcube的构造函数。因为使用了glm库,向量和矩阵的赋值上可以剩不少事~

/************************************************************************/
/* Constructor of RubikCube class
 /************************************************************************/
RubikCube::RubikCube(int _n):n(_n)
{
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            for (int k = 0; k < n; ++k)
            {
                magicCube[i][j][k].pos = glm::vec3(cubeSize / 2 + i * cubeSize,
                      cubeSize / 2 + j * cubeSize, cubeSize / 2 + k * cubeSize);
                magicCube[i][j][k].pos -= glm::vec3(cubeSize * n / 2);
                magicCube[i][j][k].matrix = glm::translate(magicCube[i][j][k].matrix, glm::vec3(0.0f, 0.0f, 0.0f));
            }
        }
    }
    //Construct layers
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; j++) {
            for (int k = 0; k < n; k++) {
                layers[0][i].cubes[j][k] = glm::vec3(i, j, n - 1 - k);
                layers[1][i].cubes[j][k] = glm::vec3(j, i, n - 1 - k);
                layers[2][i].cubes[j][k] = glm::vec3(j, k, i);
            }
        }
        layers[0][i].axis = glm::vec3(1.0, 0.0, 0.0);
        layers[1][i].axis = glm::vec3(0.0, 1.0, 0.0);
        layers[2][i].axis = glm::vec3(0.0, 0.0, 1.0);
    }
}

然后把cube们都依次绘制一下(先暂时不管内外和各面是否可见),就得到我们魔方的雏形啦。

        // render rubikcubes
        glBindVertexArray(VAO);
        for (int i = 0; i < myCube.n; ++i)
        {
            for (int j = 0; j < myCube.n; j++) {
                for (int k = 0; k < myCube.n; k++) {
                    glm::mat4 model = glm::mat4(1.0f);
                    model = glm::translate(model, myCube.magicCube[i][j][k].pos);
                    ourShader.setMat4("model", model);
                    glDrawArrays(GL_TRIANGLES, 0, 36);
                }
            }
        }

rotated rubikcube

by ERIN.Z

2024 © typecho & elise