|
一、项目简介
使用OpenGL(glfw, glad)模拟太阳系(地球、月球、太阳)系统,涉及三维图形变换、坐标系统,光照模型(待添加)、键盘鼠标事件处理等内容,在main函数中封装了绝大部分的OpenGL接口,极大的减少了代码量,操作简便。
二、代码特点
Shader.h Camera.h与Texture.h封装着色器、摄像机与纹理类,其中纹理类实例化即完成生成纹理与绑定、设置纹理环绕方式与过滤等工作。
VertexArray,VertexBuffer与IndexBuffer封装VAO, VBO, EBO,类实例化时即自动生成顶点数组对象、顶点缓冲对象、元素缓冲对象,并提供绑定与解绑等接口。VertexBufferLayout类可自由设置顶点属性。
Sphere类获得绘制球体所需的所有顶点坐标以及索引缓冲数组,用于实例化VBO和EBO对象,可手动调节设置球体绘制的精细程度。
通过Renderer类的Draw方法实现球体绘制功能。
三、源码实现及详细说明
GitHub源码地址:wonderful2643/SolarSystem (github.com)
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include &#34;Shader.h&#34;
#include &#34;Sphere.h&#34;
#include &#34;Config.h&#34;
#include &#34;Camera.h&#34;
#include &#34;Renderer.h&#34;
#include &#34;Texture.h&#34;
#include &#34;VertexArray.h&#34;
#include &#34;VertexBuffer.h&#34;
#include &#34;IndexBuffer.h&#34;
#include &#34;VertexBufferLayout.h&#34;
#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, &#34;Solar System&#34;, nullptr, nullptr);
if (!window)
{
std::cout << &#34;Failed to create GLFW window&#34; << 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 << &#34;Failed to initialize GLAD&#34; << 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(&#34;res/shader/task3.vs&#34;, &#34;res/shader/task3.fs&#34;);
shader.Bind();
shader.setInt(&#34;u_Texture&#34;, 0);
Texture textureSun(&#34;res/textures/sun.jpg&#34;);
Texture textureEarth(&#34;res/textures/earth.jpg&#34;);
Texture textureMoon(&#34;res/textures/moon.jpg&#34;);
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(&#34;u_MVP&#34;, 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(&#34;u_MVP&#34;, 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(&#34;u_MVP&#34;, 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的博客-CSDN博客_glfw实现日地月
OpenGL入门三——变换进阶
LearnOpenGL CN
【【译】TheCherno-OpenGL系列教程】 |
|