找回密码
 注册会员
查看: 192|回复: 0

OpenGL太阳系行星系统简单实现

[复制链接]
online_member 发表于 2023-2-21 22:05:09 | 显示全部楼层 |阅读模式
一、项目简介

使用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 "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));
}
四、效果展示及项目框架


OpenGL太阳系行星系统简单实现734 / 作者:123457287 / 帖子ID:112604

参考链接

C++ 实现太阳系行星系统(OpenGL)
OpenGL画球面(6) - 邗影 - 博客园 (cnblogs.com)
openGL编程学习(3):太阳、地球、月亮(含自转和公转)和航天飞机_LynnJute的博客-CSDN博客_glfw实现日地月
OpenGL入门三——变换进阶
LearnOpenGL CN
【【译】TheCherno-OpenGL系列教程】
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

手机版|UFO中文网

GMT+8, 2024-12-22 16:30

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表