# OpenGL 实现简易的星球旋转效果

## 功能要求

1. 使用不同尺寸的线框球体（Wire Sphere）表示大小两个星球
2. 使用平移和旋转操作实现小星球自转和绕大星球旋转的功能，键盘事件响应如下： d 和 shift+d: 分别控制小星球正反两个方向的自转； y 和 shift+y: 分别控制小星球绕大星球的正方向和反方向旋转
3. 语言不限，开发平台不限。具体效果展示允许略有差异。
4. 要求使用 OpenGL 着色器编程方式实现程序。

## Project3

### 原理

#### 二维坐标下绕原点自转

$aPos’=(aPos.xdayd.x-aPos.ydayd.y, aPos.ydayd.x+aPos.xdayd.y)$

#### 二维坐标下绕原点公转

$aPos’=aPos+yeard$

### 源代码main.cpp

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <cmath>

int year = 0, day = 0;

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

// settings
const unsigned int SCR_WIDTH = 1024;
const unsigned int SCR_HEIGHT = 768;

const char* vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"uniform vec2 yeard;\n"
"uniform vec2 dayd;\n"
"void main()\n"
"{\n"
"if(aPos.x*aPos.x+ aPos.y * aPos.y+ aPos.x * aPos.y<0.06)\n"
"   gl_Position = vec4(aPos.x*dayd.x-aPos.y*dayd.y+yeard.x, aPos.y*dayd.x+aPos.x*dayd.y+yeard.y, aPos.z, 1.0);\n"
"else\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";

int main()
{
//glfw初始化
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);//MacOS

//glfw window creation
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "17341163 WuK", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

//glad: load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}

//build and compile 着色器程序

//顶点着色器
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
//检查顶点着色器是否编译错误
int  success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
else {
std::cout << "vertexShader complie SUCCESS" << std::endl;
}
//片段着色器
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
//检查片段着色器是否编译错误
glGetShaderiv(fragmentShader, GL_LINK_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
else {
std::cout << "fragmentShader complie SUCCESS" << std::endl;
}

//连接到着色器程序
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
//检查片段着色器是否编译错误
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
else {
std::cout << "shaderProgram complie SUCCESS" << std::endl;
}
//连接后删除
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

const double r1 = 0.2,r2=0.3,r3=0.7,pi=acos(-1);
const int num = 9;

float vertices[(2+num)*3*2] = {
0.0f, -0.1f, sqrt(r1*r1-0.01),
0.0f, 0.1f, -sqrt(r1*r1-0.01)
};
unsigned int indices[num * 2 * 3 * 2];
for (int i = 0; i < num; ++i)
{
vertices[i * 3 + 6] = r1*cos(2*pi*i/num);
vertices[i * 3 + 1 + 6] = r1*sin(2 * pi * i / num);
vertices[i * 3 + 2 + 6] = 0;
}
for (int i = 0; i < (2 + num) * 3; ++i)
vertices[i + (2 + num) * 3] = r2 / r1 * vertices[i];
for (int i = 0; i < num; ++i)
{
int j = (i + 1) % num;
indices[2 * i * 3]= indices[2 * i * 3 + 3] = i+2 ;
indices[2 * i * 3 + 1] = indices[2 * i * 3+ 4]= j+2;
indices[2 * i * 3 + 2] = 0 ;
indices[2 * i * 3 + 5] = 1 ;

indices[2 * i * 3+ num * 2 * 3] = indices[2 * i * 3 + 3+ num * 2 * 3] = i + 2+ (2 + num) * 3;
indices[2 * i * 3 + 1+ num * 2 * 3] = indices[2 * i * 3 + 4+ num * 2 * 3] = j + 2+ (2 + num) * 3;
indices[2 * i * 3 + 2+ num * 2 * 3] = 0+ (2 + num) * 3;
indices[2 * i * 3 + 5+ num * 2 * 3] = 1+ (2 + num) * 3;
}

unsigned int VBO;
glGenBuffers(1, &VBO);
unsigned int VAO;
glGenVertexArrays(1, &VAO);
unsigned int EBO;
glGenBuffers(1, &EBO);

//初始化代码（只运行一次 (除非你的物体频繁改变))
// 1. 绑定VAO
glBindVertexArray(VAO);
// 2. 把我们的顶点数组复制到一个顶点缓冲中，供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 复制我们的索引数组到一个索引缓冲中，供OpenGL使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 设定顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

//线框模式wireframe
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

// 渲染循环
while (!glfwWindowShouldClose(window))
{
// 输入
processInput(window);

// 渲染指令
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, 0);

glUseProgram(shaderProgram);//通过glUniform4f函数设置uniform值
glUniform2f(glGetUniformLocation(shaderProgram, "yeard"),r3*cos(2*pi*year/360),r3*sin(2*pi*year/360));
glUniform2f(glGetUniformLocation(shaderProgram, "dayd"), cos(2 * pi * day / 360),sin(2 * pi * day / 360));

// 检查并调用事件，交换缓冲
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);

glfwTerminate();
return 0;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window)
{
int dday=0, dyear=0;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
dday = 1;
if (glfwGetKey(window, GLFW_KEY_Y) == GLFW_PRESS)
dyear = 1;
if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||glfwGetKey(window,GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)//是否按下了返回键
dday = 360 - dday, dyear = 360 - dyear;
day = (day + dday) % 360;
year = (year + dyear) % 360;
}


## star.c

/*
gcc star.c -o star.exe -lGL -lGLU -lglut
./star.exe
*/
#include <GL/glut.h>
int year = 0, day = 0;
void display()
{
glClear(GL_COLOR_BUFFER_BIT); //清空颜色缓冲区
glPushMatrix();				  //压栈
glColor3f(1, 0.5, 0);		  //恒星是橙黄色的
glutWireSphere(3, 20, 16);	//绘制恒星
glRotatef(year, 0, 1, 0);	 //沿y轴旋转
glTranslatef(6, 0, 0);		  //移动画笔，画行星
glRotatef(day, 0, 1, 0);	  //沿y轴旋转
glColor3f(0, 0.5, 1);		  //行星是蔚蓝色的
glutWireSphere(1, 15, 12);	//绘制行星
glPopMatrix();				  //弹栈，恢复绘制坐标
glutSwapBuffers();
glFlush(); //刷新窗口以显示当前绘制图形
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h); //设置机口
glMatrixMode(GL_PROJECTION);			  //指定哪一个矩阵是当前矩阵
glLoadIdentity();
gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 20); //透视投影矩阵(fovy,aspect,zNear,zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(9, 9, 9, 0, 0, 0, 0, 1, 0); //观察者位置，观察者朝向的位置，观察者头顶位置
}
void keyboard(unsigned char key, int x, int y)
{
if (key == 'd')
day = (day + 10) % 360;
else if (key == 'D') // 大写情况下是逆向的
day = (day + 350) % 360;
else if (key == 'y')
year = (year + 10) % 360;
else if (key == 'Y') // 大写情况下是逆向的
year = (year + 350) % 360;
else
return;
glutPostRedisplay();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); //缓存模式
glutInitWindowSize(600, 600);				 //显示框的大小
glutInitWindowPosition(100, 100);			 //确定显示框左上角的位置
glutCreateWindow("17341163_吴坎_CG_HW3");
glClearColor(0, 0, 0, 0); // 初始化
glShadeModel(GL_FLAT);	//选择平面明暗模式或光滑明暗模式
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
}