返回
探索 OpenGL 中球体的公转与自转
IOS
2023-09-15 07:40:34
在浩瀚的计算机图形学世界中,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]);