【OpenGL】(1) 环境搭建:运行简单的 OpenGL 教学示例程序

news2024/11/26 10:30:41

  • 💭 写在前面:我们尽可能地让大家以 最简单粗暴且无脑的方式,带大家配置好 OpenGL 环境,并跑出我们第一个示例程序。再次声明,本专栏所有教学都是基于 Windows上使用 VS2022 (X64) 的。本专栏主要内容是关于 3D 计算机图形技术的学习,重点是学习与此技术相关的 3D 实时渲染 (3D real-time rendering) 技术。我们会以 "理论 + 实践" 的方式进行讲解,将重点介绍基于光栅化的 3D 渲染管线的计算结构,如 OpenGL / DirectX / Vulkan / Metal 等,并使用 OpenGL API 接口实现应用程序。

Step1:解压

① 首先,在你电脑的 C 盘 "C:\" 下创建一个 usr 文件夹,然后解压这个 OpenGL_Files.zip

② 打开它,将里的 OpenGL 目录拖拽(解压)到我们刚刚创建好的  C:\usr 目录下:

③ 此时,C:\usr\OpenGL 目录下应当会有 "include", "lib", "dll" 这三个文件:

Step2:VS, 启动!

启动 Visual Studio 2022 并选择 "创建新项目 (N) ",然后点击 下一步:

在 "项目名称 (J) "中,输入您要创建的项目名称。

这里我们下面的例子打算叫 3.0.Simple2DTransArrowMouse_GL。

然后在 "位置 (L) "中,选择要保存解决方案的目录位置:

Step3:解压 “教学示例程序”

将附件 3.0.Simple2DTransArrowMouse_GL.zip 解压:

将里面的东西解压出来,这么放进去:

① 将 Simple2DTransformation.cpp 和 Shaders/LoadShaders.cpp 加到" 源文件" 里。

② 将头文件 LoadShaders.h 加到 "头文件" 里。

③ 将其余的着色器文件(以 "vert "和 "frag "结尾的文件)放到 "资源文件" 里。

放进去后就像这样:

Step4:VS 内属性设置

① 在解决方案资源管理器中 右键 项目名(如图所示),然后点击 属性(R) 

② 然后选择 VC++目录,在 包含目录 的输入框中输入 C:\usr\OpenGL\include

📌 注意:打开 "编辑" 时 一定要注意 从父级或项目默认设置继承 有没有勾选上 !

③ 紧接着在 库目录 的输入框中输入:C:\usr\OpenGL\lib\x64

这里同样是要注意勾选一下 从父级或项目默认设置继承

(有坑!不勾选可能会引发 1>LINK : fatal error LNK1104: 无法打开文件“kernel32.lib” 的悲剧)

④ 然后点 链接器输入,在 附加依赖项 中添加 "glew32.lib "和 "freeglut.lib",然后单击 应用。(在后面教学的纹理映射相关代码中,我们还会在这里添加 "FreeImage.lib")。

(PS:文件名输错了会怎么样?会 100% 触发报错大礼包!沃日!烦死你!)

(这就是手残的后果!!!半天才发现哪里不对劲!)

⑤ 点击 调试环境 并输入:

PATH=C:\usr\OpenGL\dll\x64;%PATH%;

然后点击 应用

Step5:编译并运行程序

按住鼠标左键并移动鼠标,试着移动平面对象,然后按下 "ESC "键退出程序。

至此,我们的教学演示环境也就准备好了!

💬 代码演示:

#include <stdio.h> 
#include <stdlib.h> 
#include <GL/glew.h>               // GLEW库
#include <GL/freeglut.h>           // FreeGLUT 库

#include "Shaders/LoadShaders.h"   // 包含加载着色器的头文件

GLuint h_ShaderProgram;            // 着色器程序的句柄
GLint loc_ModelViewProjectionMatrix, loc_primitive_color;     // uniform变量的索引

// 只在必要时包含glm/*.hpp
//#include <glm/glm.hpp> 

#include <glm/gtc/matrix_transform.hpp>     // 包含矩阵变换函数,如平移、旋转、缩放、正交等
glm::mat4 ModelViewProjectionMatrix;
glm::mat4 ViewMatrix, ProjectionMatrix, ViewProjectionMatrix;

#define TO_RADIAN 0.01745329252f                           // 弧度转换常数
#define TO_DEGREE 57.295779513f                            // 角度转换常数
#define BUFFER_OFFSET(offset) ((GLvoid *) (offset))        // 用于偏移的宏

#define LOC_VERTEX 0                                       // 顶点坐标的位置索引

int win_width = 0, win_height = 0;                         // 窗口宽度和高度
float centerx = 0.0f, centery = 0.0f, rotate_angle = 0.0f; // 中心点坐标及旋转角度

GLfloat axes[4][2];                                        // 坐标轴的顶点坐标
GLfloat axes_color[3] = { 0.0f, 0.0f, 0.0f };              // 坐标轴颜色
GLuint VBO_axes, VAO_axes;                                 // 坐标轴的顶点缓冲对象和顶点数组对象


/* 函数申明(共23个函数)*/
void prepare_axes(void);                     // 准备坐标轴
void update_axes(void);                      // 更新坐标轴
void draw_axes(void);                        // 绘制坐标轴

void prepare_line(void);                     // 准备线条
void update_line(void);                      // 更新线条
void draw_line(void);                        // 绘制线条

void prepare_airplane(void);                 // 准备飞机
void draw_airplane(void);                    // 绘制飞机

void display(void);                                // 显示函数
void keyboard(unsigned char key, int x, int y);    // 键盘响应函数
void special(int key, int x, int y);               // 特殊键盘响应函数
void mouse(int button, int state, int x, int y);   // 鼠标响应函数
void motion(int x, int y);                         // 鼠标移动响应函数
void reshape(int width, int height);               // 窗口大小变化响应函数
void cleanup(void);                                // 清理函数
void register_callbacks(void);                     // 注册回调函数
void prepare_shader_program(void);                 // 准备着色器程序
void initialize_OpenGL(void);                      // 初始化OpenGL
void prepare_scene(void);                          // 准备场景
void initialize_renderer(void);                    // 初始化渲染器
void initialize_glew(void);                        // 初始化GLEW
void greetings(char* program_name, char messages[][256], int n_message_lines);    // 打印问候语


/* 主函数 */
#define N_MESSAGE_LINES 2
int main(int argc, char* argv[]) {
	char program_name[64] = "Simple2DTransformation_GLSL_3.0";   // 程序名称
	char messages[N_MESSAGE_LINES][256] = {      // 消息内容
		"    - Keys used: 'ESC', four arrows",   // 使用的键盘按键
		"    - Mouse used: L-click and move"     // 使用的鼠标操作
	};

	glutInit(&argc, argv);                              // 初始化GLUT库
	glutInitDisplayMode(GLUT_RGBA | GLUT_MULTISAMPLE);  // 设置显示模式
	glutInitWindowSize(1200 * 0.95, 800 * 0.95);        // 设置窗口大小
	glutInitContextVersion(4, 0);                       // 设置OpenGL上下文版本
	glutInitContextProfile(GLUT_CORE_PROFILE);          // 设置OpenGL核心模式
	glutCreateWindow(program_name);                     // 创建窗口并设置标题

	greetings(program_name, messages, N_MESSAGE_LINES); // 打印欢迎消息
	initialize_renderer();                              // 初始化渲染器

	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);   // 设置窗口关闭时的动作
	glutMainLoop();       // 进入主循环,开始渲染和事件处理
}


void prepare_axes(void) {   // 在模型坐标系中绘制坐标轴
	axes[0][0] = -win_width / 2.5f; axes[0][1] = 0.0f;    // x轴起点
	axes[1][0] = win_width / 2.5f; axes[1][1] = 0.0f;     // x轴终点
	axes[2][0] = 0.0f; axes[2][1] = -win_height / 2.5f;   // y轴起点
	axes[3][0] = 0.0f; axes[3][1] = win_height / 2.5f;    // y轴终点

	// 初始化顶点缓冲对象
	glGenBuffers(1, &VBO_axes);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_axes);
	glBufferData(GL_ARRAY_BUFFER, sizeof(axes), axes, GL_STATIC_DRAW);

	// 初始化顶点数组对象
	glGenVertexArrays(1, &VAO_axes);
	glBindVertexArray(VAO_axes);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_axes);
	glVertexAttribPointer(LOC_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

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


void update_axes(void) { // 更新坐标轴数据
	axes[0][0] = -win_width / 2.5f; axes[1][0] = win_width / 2.5f; // 更新x轴坐标
	axes[2][1] = -win_height / 2.5f; // 更新y轴起点
	axes[3][1] = win_height / 2.5f; // 更新y轴终点

	glBindBuffer(GL_ARRAY_BUFFER, VBO_axes); // 绑定顶点缓冲对象
	glBufferData(GL_ARRAY_BUFFER, sizeof(axes), axes, GL_STATIC_DRAW); // 将更新后的坐标数据传输到GPU
	glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑顶点缓冲对象
}

void draw_axes(void) { // 绘制坐标轴
	glUniform3fv(loc_primitive_color, 1, axes_color); // 设置坐标轴颜色
	glBindVertexArray(VAO_axes); // 绑定顶点数组对象
	glDrawArrays(GL_LINES, 0, 4); // 绘制线段
	glBindVertexArray(0); // 解绑顶点数组对象
}

GLfloat line[2][2]; // 线段的顶点坐标
GLfloat line_color[3] = { 1.0f, 0.0f, 0.0f }; // 线段颜色
GLuint VBO_line, VAO_line; // 线段的顶点缓冲对象和顶点数组对象

void prepare_line(void) { 	// 准备线段数据
	line[0][0] = (1.0f / 4.0f - 1.0f / 2.5f) * win_height; // 线段起点x坐标
	line[0][1] = (1.0f / 4.0f - 1.0f / 2.5f) * win_height - win_height / 4.0f; // 线段起点y坐标
	line[1][0] = win_width / 2.5f; // 线段终点x坐标
	line[1][1] = win_width / 2.5f - win_height / 4.0f; // 线段终点y坐标

	// 初始化顶点缓冲对象
	glGenBuffers(1, &VBO_line);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_line);
	glBufferData(GL_ARRAY_BUFFER, sizeof(line), line, GL_STATIC_DRAW);

	// 初始化顶点数组对象
	glGenVertexArrays(1, &VAO_line);
	glBindVertexArray(VAO_line);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_line);
	glVertexAttribPointer(LOC_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

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


void update_line(void) { 	// 更新线段数据,表示 y = x - win_height/4
	line[0][0] = (1.0f / 4.0f - 1.0f / 2.5f) * win_height; // 计算线段起点x坐标
	line[0][1] = (1.0f / 4.0f - 1.0f / 2.5f) * win_height - win_height / 4.0f; // 计算线段起点y坐标
	line[1][0] = win_width / 2.5f; // 设置线段终点x坐标
	line[1][1] = win_width / 2.5f - win_height / 4.0f; // 设置线段终点y坐标

	glBindBuffer(GL_ARRAY_BUFFER, VBO_line); // 绑定顶点缓冲对象
	glBufferData(GL_ARRAY_BUFFER, sizeof(line), line, GL_STATIC_DRAW); // 将更新后的线段数据传输到GPU
	glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑顶点缓冲对象
}

void draw_line(void) { // 在模型坐标系中绘制线段
	// 设置线段颜色
	glUniform3fv(loc_primitive_color, 1, line_color);
	glBindVertexArray(VAO_line); // 绑定顶点数组对象
	glDrawArrays(GL_LINES, 0, 2); // 绘制线段
	glBindVertexArray(0); // 解绑顶点数组对象
}



#define AIRPLANE_BIG_WING 0 // 定义大翼索引
#define AIRPLANE_SMALL_WING 1 // 定义小翼索引
#define AIRPLANE_BODY 2 // 定义机身索引
#define AIRPLANE_BACK 3 // 定义机尾索引
#define AIRPLANE_SIDEWINDER1 4 // 定义侧风雷1索引
#define AIRPLANE_SIDEWINDER2 5 // 定义侧风雷2索引
#define AIRPLANE_CENTER 6 // 定义中心索引

GLfloat big_wing[6][2] = { // 大翼顶点坐标数组
	{ 0.0, 0.0 },
	{ -20.0, 15.0 },
	{ -20.0, 20.0 },
	{ 0.0, 23.0 },
	{ 20.0, 20.0 },
	{ 20.0, 15.0 }
};

GLfloat small_wing[6][2] = { // 小翼顶点坐标数组
	{ 0.0, -18.0 },
	{ -11.0, -12.0 },
	{ -12.0, -7.0 },
	{ 0.0, -10.0 },
	{ 12.0, -7.0 },
	{ 11.0, -12.0 }
};

GLfloat body[5][2] = { // 机身顶点坐标数组
	{ 0.0, -25.0 },
	{ -6.0, 0.0 },
	{ -6.0, 22.0 },
	{ 6.0, 22.0 },
	{ 6.0, 0.0 }
};

GLfloat back[5][2] = { // 机尾顶点坐标数组
	{ 0.0, 25.0 },
	{ -7.0, 24.0 },
	{ -7.0, 21.0 },
	{ 7.0, 21.0 },
	{ 7.0, 24.0 }
};

GLfloat sidewinder1[5][2] = { // 侧风雷1顶点坐标数组
	{ -20.0, 10.0 },
	{ -18.0, 3.0 },
	{ -16.0, 10.0 },
	{ -18.0, 20.0 },
	{ -20.0, 20.0 }
};

GLfloat sidewinder2[5][2] = { // 侧风雷2顶点坐标数组
	{ 20.0, 10.0 },
	{ 18.0, 3.0 },
	{ 16.0, 10.0 },
	{ 18.0, 20.0 },
	{ 20.0, 20.0 }
};

GLfloat center[1][2] = { // 中心顶点坐标数组
	{ 0.0, 0.0 }
};

GLfloat airplane_color[7][3] = { // 飞机各部分颜色数组
	{ 150 / 255.0f, 129 / 255.0f, 183 / 255.0f },  // 大翼颜色
	{ 245 / 255.0f, 211 / 255.0f,   0 / 255.0f },  // 小翼颜色
	{ 111 / 255.0f,  85 / 255.0f, 157 / 255.0f },  // 机身颜色
	{ 150 / 255.0f, 129 / 255.0f, 183 / 255.0f },  // 机尾颜色
	{ 245 / 255.0f, 211 / 255.0f,   0 / 255.0f },  // 侧风雷1颜色
	{ 245 / 255.0f, 211 / 255.0f,   0 / 255.0f },  // 侧风雷2颜色
	{ 255 / 255.0f,   0 / 255.0f,   0 / 255.0f }   // 中心颜色
};




GLuint VBO_airplane, VAO_airplane; // 飞机的顶点缓冲对象和顶点数组对象

void prepare_airplane() {
	GLsizeiptr buffer_size = sizeof(big_wing) + sizeof(small_wing) + sizeof(body) + sizeof(back)
		+ sizeof(sidewinder1) + sizeof(sidewinder2) + sizeof(center);

	// 初始化顶点缓冲对象
	glGenBuffers(1, &VBO_airplane);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_airplane);
	glBufferData(GL_ARRAY_BUFFER, buffer_size, NULL, GL_STATIC_DRAW); // 分配缓冲区对象的内存空间

	// 将顶点数据分别存入缓冲区对象
	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(big_wing), big_wing);
	glBufferSubData(GL_ARRAY_BUFFER, sizeof(big_wing), sizeof(small_wing), small_wing);
	glBufferSubData(GL_ARRAY_BUFFER, sizeof(big_wing) + sizeof(small_wing), sizeof(body), body);
	glBufferSubData(GL_ARRAY_BUFFER, sizeof(big_wing) + sizeof(small_wing) + sizeof(body), sizeof(back), back);
	glBufferSubData(GL_ARRAY_BUFFER, sizeof(big_wing) + sizeof(small_wing) + sizeof(body) + sizeof(back),
		sizeof(sidewinder1), sidewinder1);
	glBufferSubData(GL_ARRAY_BUFFER, sizeof(big_wing) + sizeof(small_wing) + sizeof(body) + sizeof(back)
		+ sizeof(sidewinder1), sizeof(sidewinder2), sidewinder2);
	glBufferSubData(GL_ARRAY_BUFFER, sizeof(big_wing) + sizeof(small_wing) + sizeof(body) + sizeof(back)
		+ sizeof(sidewinder1) + sizeof(sidewinder2), sizeof(center), center);

	// 初始化顶点数组对象
	glGenVertexArrays(1, &VAO_airplane);
	glBindVertexArray(VAO_airplane);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_airplane);
	glVertexAttribPointer(LOC_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

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


void draw_airplane() { // 在模型坐标系中绘制飞机
	glBindVertexArray(VAO_airplane); // 绑定顶点数组对象

	// 绘制大翼
	glUniform3fv(loc_primitive_color, 1, airplane_color[AIRPLANE_BIG_WING]);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 6);

	// 绘制小翼
	glUniform3fv(loc_primitive_color, 1, airplane_color[AIRPLANE_SMALL_WING]);
	glDrawArrays(GL_TRIANGLE_FAN, 6, 6);

	// 绘制机身
	glUniform3fv(loc_primitive_color, 1, airplane_color[AIRPLANE_BODY]);
	glDrawArrays(GL_TRIANGLE_FAN, 12, 5);

	// 绘制机尾
	glUniform3fv(loc_primitive_color, 1, airplane_color[AIRPLANE_BACK]);
	glDrawArrays(GL_TRIANGLE_FAN, 17, 5);

	// 绘制侧风雷1
	glUniform3fv(loc_primitive_color, 1, airplane_color[AIRPLANE_SIDEWINDER1]);
	glDrawArrays(GL_TRIANGLE_FAN, 22, 5);

	// 绘制侧风雷2
	glUniform3fv(loc_primitive_color, 1, airplane_color[AIRPLANE_SIDEWINDER2]);
	glDrawArrays(GL_TRIANGLE_FAN, 27, 5);

	// 绘制中心点
	glUniform3fv(loc_primitive_color, 1, airplane_color[AIRPLANE_CENTER]);
	glPointSize(5.0);
	glDrawArrays(GL_POINTS, 32, 1);
	glPointSize(1.0);

	glBindVertexArray(0); // 解绑顶点数组对象
}


// 绘制飞机 1-4
void display(void) {
	int i;
	float x, r, s, delx, delr, dels;
	glm::mat4 ModelMatrix;

	glClear(GL_COLOR_BUFFER_BIT);
	
	ModelMatrix = glm::mat4(1.0f);
	ModelViewProjectionMatrix = ViewProjectionMatrix * ModelMatrix;
	glUniformMatrix4fv(loc_ModelViewProjectionMatrix, 1, GL_FALSE, &ModelViewProjectionMatrix[0][0]);
	draw_axes();
 	draw_line();
	draw_airplane();
	 
	ModelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(centerx, centery, 0.0f));
	ModelMatrix = glm::rotate(ModelMatrix, rotate_angle, glm::vec3(0.0f, 0.0f, 1.0f));
	ModelViewProjectionMatrix = ViewProjectionMatrix * ModelMatrix;
	glUniformMatrix4fv(loc_ModelViewProjectionMatrix, 1, GL_FALSE, &ModelViewProjectionMatrix[0][0]);
 	draw_airplane(); // 0
	
	ModelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(-win_width / 4.0f, -win_height / 4.0f, 0.0f));
	ModelMatrix = glm::rotate(ModelMatrix, 90.0f*TO_RADIAN, glm::vec3(0.0f, 0.0f, 1.0f));
	ModelMatrix = glm::scale(ModelMatrix, glm::vec3(3.0f, 3.0f, 1.0f));
	ModelViewProjectionMatrix = ViewProjectionMatrix * ModelMatrix;
	glUniformMatrix4fv(loc_ModelViewProjectionMatrix, 1, GL_FALSE, &ModelViewProjectionMatrix[0][0]);
	draw_airplane();  // 1
	
	ModelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(win_width / 2.5f, -win_height / 8.0f, 0.0f));
	ModelMatrix = glm::rotate(ModelMatrix, 270.0f*TO_RADIAN, glm::vec3(0.0f, 0.0f, 1.0f));
	ModelViewProjectionMatrix = ViewProjectionMatrix * ModelMatrix;
	glUniformMatrix4fv(loc_ModelViewProjectionMatrix, 1, GL_FALSE, &ModelViewProjectionMatrix[0][0]);
	draw_airplane();  // 2
 
	ModelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(win_height / 4.0f, 0.0f, 0.0f));
	ModelMatrix = glm::rotate(ModelMatrix, 45.0f*TO_RADIAN, glm::vec3(0.0f, 0.0f, 1.0f));
	ModelMatrix = glm::scale(ModelMatrix, glm::vec3(1.0f, -1.0f, 1.0f));
	ModelMatrix = glm::rotate(ModelMatrix, -45.0f*TO_RADIAN, glm::vec3(0.0f, 0.0f, 1.0f));
	ModelMatrix = glm::translate(ModelMatrix, glm::vec3(-win_height / 4.0f, 0.0f, 0.0f));
	ModelMatrix = glm::translate(ModelMatrix, glm::vec3(win_width / 2.5f, -win_height / 8.0f, 0.0f));
	ModelMatrix = glm::scale(ModelMatrix, glm::vec3(2.0f, 2.0f, 1.0f));
	ModelMatrix = glm::translate(ModelMatrix, glm::vec3(-win_width / 2.5f, win_height / 8.0f, 0.0f));
 
	ModelMatrix = glm::translate(ModelMatrix, glm::vec3(win_width / 2.5f, -win_height / 8.0f, 0.0f));
	ModelMatrix = glm::rotate(ModelMatrix, 270.0f*TO_RADIAN, glm::vec3(0.0f, 0.0f, 1.0f));
	ModelViewProjectionMatrix = ViewProjectionMatrix * ModelMatrix;
	glUniformMatrix4fv(loc_ModelViewProjectionMatrix, 1, GL_FALSE, &ModelViewProjectionMatrix[0][0]);
	draw_airplane();  // 3
	 
	delx = win_width/14.0f; delr = 15.0f; dels = 1.1f;
	x = -delx; r = delr; s = dels;
	for (i = 0; i < 5; i++, x -= delx, r += delr, s *= dels) {
		ModelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(x, 15.0f*sqrtf(-x), 0.0f));
		ModelMatrix = glm::rotate(ModelMatrix, r*TO_RADIAN, glm::vec3(0.0f, 0.0f, 1.0f));
			glTranslatef(x, 15.0f*sqrtf(-x), 0.0f);
			ModelMatrix = glm::scale(ModelMatrix, glm::vec3(s, s, 1.0f));
			ModelViewProjectionMatrix = ViewProjectionMatrix * ModelMatrix;
			glUniformMatrix4fv(loc_ModelViewProjectionMatrix, 1, GL_FALSE, &ModelViewProjectionMatrix[0][0]);
			draw_airplane();  // 4
	}
	glFlush();	
}   
void keyboard(unsigned char key, int x, int y) {
	switch (key) {
	case 27: // ESC键
		glutLeaveMainLoop(); // 调用退出主循环,触发清理回调函数
		break;
	}
}

void special(int key, int x, int y) {
#define SENSITIVITY 2.0 // 灵敏度
	switch (key) {
	case GLUT_KEY_LEFT: // 左箭头键
		centerx -= SENSITIVITY; // 水平移动
		glutPostRedisplay(); // 重新绘制窗口
		break;
	case GLUT_KEY_RIGHT: // 右箭头键
		centerx += SENSITIVITY; // 水平移动
		glutPostRedisplay(); // 重新绘制窗口
		break;
	case GLUT_KEY_DOWN: // 下箭头键
		centery -= SENSITIVITY; // 垂直移动
		glutPostRedisplay(); // 重新绘制窗口
		break;
	case GLUT_KEY_UP: // 上箭头键
		centery += SENSITIVITY; // 垂直移动
		glutPostRedisplay(); // 重新绘制窗口
		break;
	}
}

int leftbuttonpressed = 0; // 左键是否按下

void mouse(int button, int state, int x, int y) {
	if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN)) // 鼠标左键按下
		leftbuttonpressed = 1; // 设置左键按下标志为1
	else if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_UP)) // 鼠标左键释放
		leftbuttonpressed = 0; // 设置左键按下标志为0
}

void motion(int x, int y) {
	static int delay = 0; // 鼠标移动延迟计数器
	static float tmpx = 0.0, tmpy = 0.0; // 临时存储鼠标位置的变量
	float dx, dy; // 鼠标移动的水平和垂直距离

	if (leftbuttonpressed) { // 如果左键按下
		centerx = x - win_width / 2.0f; // 计算鼠标的x坐标
		centery = (win_height - y) - win_height / 2.0f; // 计算鼠标的y坐标

		if (delay == 8) {	// 如果鼠标移动计数器为8
			dx = centerx - tmpx; // 计算水平移动距离
			dy = centery - tmpy; // 计算垂直移动距离

			if (dx > 0.0) { // 如果水平移动距离为正
				rotate_angle = atan(dy / dx) + 90.0f * TO_RADIAN; // 计算旋转角度
			}
			else if (dx < 0.0) { // 如果水平移动距离为负
				rotate_angle = atan(dy / dx) - 90.0f * TO_RADIAN; // 计算旋转角度
			}
			else if (dx == 0.0) { // 如果水平移动距离为0
				if (dy > 0.0) // 如果垂直移动距离为正
					rotate_angle = 180.0f * TO_RADIAN; // 设置旋转角度为180度
				else  // 如果垂直移动距离为负
					rotate_angle = 0.0f; // 设置旋转角度为0度
			}
			tmpx = centerx; // 更新临时存储的鼠标x坐标
			tmpy = centery; // 更新临时存储的鼠标y坐标
			delay = 0; // 重置鼠标移动延迟计数器
		}
		glutPostRedisplay(); // 重新绘制窗口
		delay++; // 增加鼠标移动延迟计数器
	}
}



void reshape(int width, int height) {
	win_width = width; // 更新窗口宽度
	win_height = height; // 更新窗口高度

	glViewport(0, 0, win_width, win_height); // 设置视口
	ProjectionMatrix = glm::ortho(-win_width / 2.0, win_width / 2.0,
		-win_height / 2.0, win_height / 2.0, -1000.0, 1000.0); // 设置正交投影矩阵
	ViewProjectionMatrix = ProjectionMatrix * ViewMatrix; // 更新视图投影矩阵

	update_axes(); // 更新坐标轴
	update_line(); // 更新线段

	glutPostRedisplay(); // 重新绘制窗口
}

void cleanup(void) {
	glDeleteVertexArrays(1, &VAO_axes); // 删除坐标轴顶点数组对象
	glDeleteBuffers(1, &VBO_axes); // 删除坐标轴顶点缓冲对象

	glDeleteVertexArrays(1, &VAO_line); // 删除线段顶点数组对象
	glDeleteBuffers(1, &VBO_line); // 删除线段顶点缓冲对象

	glDeleteVertexArrays(1, &VAO_airplane); // 删除飞机顶点数组对象
	glDeleteBuffers(1, &VBO_airplane); // 删除飞机顶点缓冲对象
}

void register_callbacks(void) {
	glutDisplayFunc(display); // 注册显示回调函数
	glutKeyboardFunc(keyboard); // 注册键盘按键回调函数
	glutSpecialFunc(special); // 注册特殊键回调函数
	glutMouseFunc(mouse); // 注册鼠标点击回调函数
	glutMotionFunc(motion); // 注册鼠标移动回调函数
	glutReshapeFunc(reshape); // 注册窗口大小变化回调函数
	glutCloseFunc(cleanup); // 注册窗口关闭回调函数
}

void prepare_shader_program(void) {
	ShaderInfo shader_info[3] = { // 着色器信息
		{ GL_VERTEX_SHADER, "Shaders/simple.vert" }, // 顶点着色器
		{ GL_FRAGMENT_SHADER, "Shaders/simple.frag" }, // 片段着色器
		{ GL_NONE, NULL } // 结束标记
	};

	h_ShaderProgram = LoadShaders(shader_info); // 加载着色器程序
	glUseProgram(h_ShaderProgram); // 使用着色器程序

	loc_ModelViewProjectionMatrix = glGetUniformLocation(h_ShaderProgram, "u_ModelViewProjectionMatrix"); // 获取模型视图投影矩阵的位置
	loc_primitive_color = glGetUniformLocation(h_ShaderProgram, "u_primitive_color"); // 获取基本颜色的位置
}

void initialize_OpenGL(void) {
	glEnable(GL_MULTISAMPLE); // 启用多重采样
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // 设置多边形渲染模式为填充

	glClearColor(44 / 255.0f, 180 / 255.0f, 49 / 255.0f, 1.0f); // 设置清除颜色为深绿色
	ViewMatrix = glm::mat4(1.0f); // 初始化视图矩阵
}

void prepare_scene(void) {
	prepare_axes(); // 准备坐标轴
	prepare_line(); // 准备线段
	prepare_airplane(); // 准备飞机模型
}

void initialize_renderer(void) {
	register_callbacks(); // 注册回调函数
	prepare_shader_program(); // 准备着色器程序
	initialize_OpenGL(); // 初始化OpenGL状态
	prepare_scene(); // 准备场景对象
}

void initialize_glew(void) {
	GLenum error;

	glewExperimental = GL_TRUE; // 启用实验性的GLEW功能

	error = glewInit(); // 初始化GLEW
	if (error != GLEW_OK) { // 如果初始化失败
		fprintf(stderr, "Error: %s\n", glewGetErrorString(error)); // 打印错误信息
		exit(-1); // 退出程序
	}
	fprintf(stdout, "*********************************************************\n");
	fprintf(stdout, " - GLEW version supported: %s\n", glewGetString(GLEW_VERSION)); // 打印支持的GLEW版本
	fprintf(stdout, " - OpenGL renderer: %s\n", glGetString(GL_RENDERER)); // 打印OpenGL渲染器信息
	fprintf(stdout, " - OpenGL version supported: %s\n", glGetString(GL_VERSION)); // 打印支持的OpenGL版本
	fprintf(stdout, "*********************************************************\n\n");
}

void greetings(char* program_name, char messages[][256], int n_message_lines) {
	fprintf(stdout, "**************************************************************\n\n");
	fprintf(stdout, "  PROGRAM NAME: %s\n\n", program_name); // 打印程序名称

	for (int i = 0; i < n_message_lines; i++)
		fprintf(stdout, "%s\n", messages[i]); // 逐行打印消息内容
	fprintf(stdout, "\n**************************************************************\n\n");

	initialize_glew(); // 初始化GLEW
}

📌 [ 笔者 ]   雷向明
📃 [ 更新 ]   2024.3.31
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考文献:

– J. Hughes et al., Computer Graphics: Principles and Practice(3rd ed.), Addison-Wesley, 2013.

– S. Marscner et al., Fundamentals of Computer Graphics(4th ed.), CRC Press, 2015.

– E. Angel, Interactive Computer Graphics: A Top-Down Approach with Shader-Based OpenGL (7th ed.), Addison-Wesley, 2014.

– T. Akenine-Möller et al., Real-Time Rendering(4th ed.), AK Peters/CRC Press, 2018.

– D. Shreiner et al., OpenGL Programming Guide(9th ed.): The Official Guide to Learning OpenGL, Versions 4.5 with SPIR-V, 2016.

– G. Sellers and R. Wright Jr., OpenGL Superbible: Comprehensive Tutorial and reference(7th ed.), Addison-Wesley Professional, 2015.

– J. de Vries, Learn OpenGL – Graphics Programming, Kendall & Welling, 2020.

– D. Wolff, OpenGL 4 Shading Language Cookbook(3nd ed.), Packt Publishing, 2018.

– D. Ginsburg et al., OpenGL ES 3.0 Programming Guide(2nd ed.), Addison-Wesley, 2014

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1558856.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【讲解下Gitea】

&#x1f308;个人主页:程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

anaconda navigator updater 版本更新失败

打开后&#xff0c;更新界面持续很久 使用命令行查看版本 执行conda update anaconda-navigator 第一次执行中间失败&#xff0c;重新执行&#xff0c;更新成功

基于深度学习YOLOv8+PyQt5的水底海底垃圾生物探测器检测识别系统(源码+数据集+配置说明)

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;323海底 获取完整源码7000张数据集配置说明文件说明远程操作配置环境跑通程序 效果展示 基于深度学习YOLOv8PyQt5的水底海底垃圾生物探测器检测识别系统设计&#xff08;源码数据集配置文件&#xff09; 各文件说明 程序运…

网页布局案例 浮动

这里主要讲浮动 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>*{padding: 0;margin: 0;}.header{height: 40px;background-color: #333;}.nav{width: 1226px;heig…

【Java 多线程】从源码出发,剖析Threadlocal的数据结构

文章目录 exampleset(T value)createMap(t, value);set(ThreadLocal<?> key, Object value)ThreadLocalMap和Thread的关系 全貌 ThreadLocal是个很重要的多线程类&#xff0c;里面数据结构的设计很有意思&#xff0c;很巧妙。但是我们平时使用它的时候常常容易对它的使用…

学习【Redis高级篇】这一篇就够了

目录 1. 分布式缓存1-1. Redis持久化RDB持久化AOF持久化RDB与AOF对比总结 1-2. Redis主从集群全量同步增量同步主从优化总结 1-3. Redis哨兵哨兵作用集群监控原理集群故障恢复原理RedisTemplate访问哨兵总结 1-4. Redis分片集群散列插槽集群伸缩故障转移 2. 多级缓存2-1. 浏览器…

linux ubuntu 在保存文件不被允许,但是root权限

现象&#xff1a;MobaXterm_Personal_2登录到服务器&#xff0c;切换到root用户&#xff0c;然后使用MobaXterm_Personal_2自带的编辑器&#xff0c;编写文件&#xff0c;进行保存不被允许&#xff1b;查看目录root是有权限进行修改文件的&#xff0c;然后使用vim进行修改保存&…

【AcWing】蓝桥杯集训每日一题Day7|贡献法|4261.孤独的照片(C++)

4261.孤独的照片 AcWing 4261. 孤独的照片&#xff08;每日一题&#xff09; - AcWing难度&#xff1a;简单时/空限制&#xff1a;1s / 64MB总通过数&#xff1a;9889总尝试数&#xff1a;26088来源&#xff1a;USACO 2021 December Contest Bronze算法标签贡献法乘法原理 题目…

服务器安全事件应急响应排查方法

针对服务器操作系统的安全事件也非常多的。攻击方式主要是弱口令攻击、远程溢出攻击及其他应用漏洞攻击等。分析安全事件&#xff0c;找到入侵源&#xff0c;修复漏洞&#xff0c;总结经验&#xff0c;避免再次出现安全事件&#xff0c;以下是参考网络上文章&#xff0c;总结的…

视觉里程计之对极几何

视觉里程计之对极几何 前言 上一个章节介绍了视觉里程计关于特征点的一些内容&#xff0c;相信大家对视觉里程计关于特征的描述已经有了一定的认识。本章节给大家介绍视觉里程计另外一个概念&#xff0c;对极几何。 对极几何 对极几何是立体视觉中的几何关系&#xff0c;描…

Ruoyi-Cloud-Plus_使用Docker部署分布式微服务系统_环境准备_001---SpringCloud工作笔记200

1.首先安装docker: 如果以前安装过首先执行: yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine 去卸载docker 2.安装dokcer需要的工具包…

校园跑腿(源码+文档)

校园跑腿管理系统&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明含有功能项目截图客户端店铺代购用户条款隐私协议租借服务行李代搬拨打客服电话注册界面我的界面申请骑手登录界面快递带取资料修改快递代寄主页万能帮 管理端代购管理添加用户订单…

Base64编码的全面介绍

title: Base64编码的全面介绍 date: 2024/3/31 18:55:49 updated: 2024/3/31 18:55:49 tags: Base64编码网络传输文本转换数据膨胀非加密性质应用场景安全传输 1. Base64的定义和作用 Base64是一种用64个字符表示二进制数据的编码方式&#xff0c;通常用于在网络传输中将二进…

SpringBoot接收参数的方式

Get 请求 1.1 以方法的形参接收参数 1.这种方式一般适用参数比较少的情况 RestController RequestMapping("/user") Slf4j public class UserController {GetMapping("/detail")public Result<User> getUserDetail(String name,String phone) {log.…

【回溯与邻里交换】纸牌三角

1.回溯算法 旋转有3种可能&#xff0c;镜像有2种 所以最后次数&#xff1a;counts/3/2 #include<iostream> using namespace std;int num[9]; int counts0; bool bools[9];//默认为false int dfs(int step){if(step9){//索引 if((num[0]num[1]num[2]num[3]num[3]num[4]n…

Docker 轻量级可视化工具 Portainer

1. 是什么 它是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便管理Docker环境&#xff0c;也包括单机环境和集群环境。 2. 安装 官网&#xff1a;Kubernetes and Docker Container Management Software 安装路径&#xff1a;Install the Compose plug…

微分方程数值解法_常微分方程篇

一阶常微分方程初值问题 问题的适定性 (well-posedness): (數學系的角度) • 存在性:问题有解 • 唯一性:解是唯一的 • 稳定性:这个唯一解连续地依赖于问题中所给的数据(即初值、边值等) 初值问题的求解 Euler 法 區別(極限) 入門 要點:極限、中值定理==>差分方程…

leetcode131分割回文串

递归树 下面这个代码是遍历处所有的子串 #include <bits/stdc.h> using namespace std; class Solution { public:vector<vector<string>> vvs;vector<string> vs;vector<vector<string>> partition(string s) {dfs(0,s);return vvs;}vo…

Redis持久化 RDB AOF

前言 redis的十大类型终于告一段落了,下面我们开始redis持久化新篇章 为啥需要持久化呢? 我们知道redis是挡在mysql前面的带刀侍卫 是在内存中的,假如我们的redis宕机了,难道数据直接冲入mysql??? 这显然是不可能的,mysql肯定扛不住这样的场景,所以我们有了redis持久化策略…

2014年认证杯SPSSPRO杯数学建模A题(第二阶段)轮胎的花纹全过程文档及程序

2014年认证杯SPSSPRO杯数学建模 A题 轮胎的花纹 原题再现&#xff1a; 轮胎被广泛使用在多种陆地交通工具上。根据性能的需要&#xff0c;轮胎表面常会加工出不同形状的花纹。在设计轮胎时&#xff0c;往往要针对其使用环境&#xff0c;设计出相应的花纹形状。   第二阶段问…