OpenGL太阳系行星系统简单实现
一、项目简介
采用glfw和glad来构建一个包含三维变换和坐标体系的太阳系系统。该系统涉及包括三维图形变换、坐标系统以及光照模型等内容,在主函数中将大部分OpenGL接口进行了封装,并大幅减少了代码规模的同时使得操作更加简便。
二、代码特点
在Shader.h和Camera.h中封装了着色器、摄像机以及纹理类。其中,在实例化完成时,则完成了生成对应 textures 并与其绑定的任务,并对 textures 的环绕方式进行设置以及过滤效果进行了优化处理。这些操作使得整体编码复杂度得到了显著降低。
VertexArray、VertexBuffer和IndexBuffer被封装为VAO、VBO和EBO,并在类 instantiation过程中自动生成相应的顶点数组对象、顶点缓冲对象和元素缓冲对象,并提供了绑定与解绑的相关接口;VertexBufferLayout类允许用户自由配置顶点属性设置
Sphere类通过获取绘制球体所需的所有顶点坐标以及索引缓冲数组来实现实例化VBO和EBO对象的过程,并且可以根据需求进行微调以调整球体绘制的精细程度。
通过Renderer类的Draw方法实现球体绘制。
三、源码实现及详细说明
存储位置:wonderful2643/SolarSystem (github.com)
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#include "Sphere.h"
#include "Config.h"
#include "Camera.h"
#include "Renderer.h"
#include "Texture.h"
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "VertexBufferLayout.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
// camera
Camera camera(glm::vec3(0.0f, 5.0f, 35.0f));
float lastX = WindowWidth / 2.0f;
float lastY = WindowHeight / 2.0f;
bool firstMouse = true;
// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(WindowWidth, WindowHeight, "Solar System", nullptr, nullptr);
if (!window)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
//注释该行,调试出bug键盘鼠标不会卡死
//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSwapInterval(1);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
std::cout << glGetString(GL_VERSION) << std::endl;
//获得球体所有顶点坐标以及EBO数组
Sphere mySphere(40);
std::vector<float> sphereVertices = mySphere.getVertices();
std::vector<unsigned int> sphereIndices = mySphere.getIndices();
{
//VAO
VertexArray va;
//VBO
VertexBuffer vb(&sphereVertices[0], sphereVertices.size() * sizeof(float));
VertexBufferLayout layout;
//顶点属性布局:前三位为球体上点的x, y, z坐标,后两位为2D纹理坐标
layout.Push<float>(3);
layout.Push<float>(2);
va.AddBuffer(layout);
IndexBuffer ib(&sphereIndices[0], sphereIndices.size());//放在最后
glEnable(GL_DEPTH_TEST);
Shader shader("res/shader/task3.vs", "res/shader/task3.fs");
shader.Bind();
shader.setInt("u_Texture", 0);
Texture textureSun("res/textures/sun.jpg");
Texture textureEarth("res/textures/earth.jpg");
Texture textureMoon("res/textures/moon.jpg");
vb.Unbind();
va.Unbind();
ib.Unbind();
shader.Unbind();
Renderer render;
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 proj = glm::perspective(glm::radians(camera.Zoom), (float)WindowWidth / (float)WindowHeight, 0.1f, 100.0f);
// 渲染循环
while (!glfwWindowShouldClose(window))
{
float currentFrame = static_cast<float>(glfwGetTime());
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
processInput(window);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
va.Bind();
{
// Sun
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::rotate(glm::mat4(1.0f), -(float)glfwGetTime() / 5, glm::vec3(0.0f, 1.0f, 0.0f));
model = scale(model, glm::vec3(sunScale, sunScale, sunScale));
glm::mat4 mvp = proj * view * model;
textureSun.Bind();
shader.Bind();
shader.setMat4("u_MVP", mvp);
render.Draw(mySphere.getNumIndices());
}
{
// Earth
glm::mat4 model = glm::mat4(1.0f);
// 公转
model = glm::rotate(model, (float)glfwGetTime() / 1.5f, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::translate(model, glm::vec3(SunEarthDistance, .0f, .0f));
// 抵消公转对自身倾斜方向的影响,保证公转后 仍然向右倾斜
model = glm::rotate(model, -(float)glfwGetTime(), glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::rotate(model, -glm::radians(ErothAxialAngle), glm::vec3(0.0, 0.0, 1.0));
// 自转
model = glm::rotate(model, -(float)glfwGetTime(), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 mvp = proj * view * model;
textureEarth.Bind();
shader.Bind();
shader.setMat4("u_MVP", mvp);
render.Draw(mySphere.getNumIndices());
}
{
// Moon
glm::mat4 model = glm::mat4(1.0f);
// 地日公转
model = glm::rotate(model, (float)glfwGetTime() / 1.5f, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::translate(model, glm::vec3(SunEarthDistance, .0f, .0f));
// 月球公转
model = glm::rotate(model, (float)glfwGetTime() * 2.0f, glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::translate(model, glm::vec3(MoonEarthDistance, 0.0, 0.0));
// 月球自转
model = glm::rotate(model, -(float)glfwGetTime(), glm::vec3(0.0f, 1.0f, 0.0f));
model = scale(model, glm::vec3(moonScale, moonScale, moonScale));
glm::mat4 mvp = proj * view * model;
textureMoon.Bind();
shader.Bind();
shader.setMat4("u_MVP", mvp);
render.Draw(mySphere.getNumIndices());
}
glfwSwapBuffers(window);
glfwPollEvents();
}
}
glfwTerminate();
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);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
float xpos = static_cast<float>(xposIn);
float ypos = static_cast<float>(yposIn);
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(static_cast<float>(yoffset));
}
代码解读
四、效果展示及项目框架

参考链接
开发太阳系行星系统的C++代码(OpenGL技术)
OpenGL画球面(6) - 邗影 - 博客园 (cnblogs.com)
OpenGL编程学习(第3课): 太阳 地球 月亮(包括自转与公转)以及航天飞机_LynnJute的博客-博客_glfw实现日地月
LearnOpenGL CN:官方文档和资源库
LearnOpenGL CN:官方文档和资源库
【译
基于Cherno和OpenGL的技术教学官方翻译版
基于Cherno和OpenGL的技术教学官方翻译版
基于Cherno和OpenGL的技术教学官方翻译版
