返回

探索 OpenGL 中球体的公转与自转

IOS

在浩瀚的计算机图形学世界中,OpenGL 扮演着至关重要的角色,它赋予开发者操纵三维场景并生成逼真图形的能力。在 OpenGL 的画布上,我们可以描绘出引人入胜的画面,让物体栩栩如生。在这篇文章中,我们将深入探讨如何利用 OpenGL 实现球体的公转和自转,为我们的三维场景增添动态与活力。

铺垫

在我们踏上创造旋转球体的旅程之前,让我们先对一些基本概念进行复习。OpenGL 使用一种称为矩阵的数学结构来表示和操作三维空间中的对象。矩阵可以缩放、旋转和移动物体,从而让我们能够控制它们的精确位置和方向。

着色器是 OpenGL 中的程序,它们负责计算每个像素的颜色。顶点着色器处理顶点数据,而片段着色器处理片段数据。通过精心编写的着色器,我们可以为场景中的对象赋予不同的材质、纹理和光照效果。

搭建 OpenGL 环境

要开始我们的旅程,我们需要设置 OpenGL 环境。这涉及创建一个 OpenGL 上下文,初始化着色器程序,并指定清除颜色和深度缓冲区。

// 创建 OpenGL 上下文
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL 球体公转自转", nullptr, nullptr);

// 初始化着色器程序
GLuint program = glCreateProgram();
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

// 指定清除颜色和深度缓冲区
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);

绘制球体

有了 OpenGL 环境之后,我们就可以开始绘制球体了。我们将使用三角形网格来近似表示球体,并使用顶点和法线数据来定义其表面。

// 定义球体顶点和法线数据
GLfloat vertices[] = {
    // 前面
    0.0f, 0.0f, 1.0f,
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    // 右面
    1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f,
    // 后面
    0.0f, 0.0f, -1.0f,
    -1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    // 左面
    -1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, -1.0f,
    0.0f, 1.0f, 0.0f,
    // 上面
    0.0f, 1.0f, 0.0f,
    1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f,
    // 下面
    0.0f, -1.0f, 0.0f,
    1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, -1.0f
};

GLfloat normals[] = {
    // 前面
    0.0f, 0.0f, 1.0f,
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    // 右面
    1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f,
    // 后面
    0.0f, 0.0f, -1.0f,
    -1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    // 左面
    -1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, -1.0f,
    0.0f, 1.0f, 0.0f,
    // 上面
    0.0f, 1.0f, 0.0f,
    1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f,
    // 下面
    0.0f, -1.0f, 0.0f,
    1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, -1.0f
};

// 创建和绑定顶点数组对象和顶点缓冲对象
GLuint vao, vbo, nbo;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &nbo);
glBindBuffer(GL_ARRAY_BUFFER, nbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(normals), normals, GL_STATIC_DRAW);

// 启用顶点和法线数组
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

公转和自转

现在,球体已经绘制完成,我们可以对其进行公转和自转。公转是指球体围绕另一个物体(通常是一个更大的球体)的圆形轨迹运动。自转是指球体绕其自身轴的旋转运动。

要实现公转,我们将使用旋转矩阵对球体进行绕着某个点旋转的变换。要实现自转,我们将使用另一个旋转矩阵对球体进行绕着自身轴旋转的变换。

// 公转矩阵
glm::mat4 orbitMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(glfwGetTime()), glm::vec3(0.0f, 1.0f, 0.0f));

// 自转矩阵
glm::mat4 spinMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(glfwGetTime()), glm::vec3(1.0f, 0.0f, 0.0f));

// 将模型矩阵发送到顶点着色器
GLuint modelMatrixLoc = glGetUniformLocation(program, "modelMatrix");
glUniformMatrix4fv(modelMatrixLoc, 1, GL_FALSE, &orbitMatrix[0][0]);

// 渲染球体
glDrawArrays(GL_TRIANGLES, 0, 36);

渲染循环

最后,我们使用一个渲染循环来持续渲染场景。在每个循环中,我们都会清除缓冲区,更新矩阵,渲染球体,然后交换缓冲区。

while (!glfwWindowShouldClose(window)) {
    // 清除缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 更新矩阵
    orbitMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(glfwGetTime()), glm::vec3(0.0f, 1.0f, 0.0f));
    spinMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(glfwGetTime()), glm::vec3(1.0f, 0.0f, 0.0f));
    glUniformMatrix4fv(modelMatrixLoc, 1, GL_FALSE, &orbitMatrix[0][0]);