- 💭 写在前面:我们尽可能地让大家以 最简单粗暴且无脑的方式,带大家配置好 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 |