在现代计算机图形学中,OpenGL及其相关的开源库扮演着至关重要的角色。这些库提供了丰富的功能和工具,使得开发者可以轻松地创建复杂的图形应用程序。这里总结的探讨一下OpenGL、GLEW、GLFW、GLM、Assimp以及GL、GLUT、FreeGLUT、GLAD等库之间的联系和概念,以及它们在图形编程中的作用。
OpenGL及其基础概念
OpenGL(Open Graphics Library)是一种跨平台的图形API,用于渲染2D和3D图形。它提供了一组用于渲染图形的函数,允许开发人员利用GPU的强大性能来创建复杂的图形效果。OpenGL是一个开放标准,由Khronos Group维护和更新。OpenGL只有框架没有实现,因为OpenGL只有函数声明没有源文件实现,类似于接口和虚函数,所有的实现是显卡生产商提供。比如NVIDIA或者AMD就要自己实现OpenGL函数内容,所以不同的生产商可以对自己的产品提供优化。OpenGL函数库相关的API有核心库(gl),实用库(glu),辅助库(aux)、实用工具库(glut),窗口库(glx、agl、wgl)和扩展函数库等。gl是核心,glu是对gl的部分封装,glx、agl、wgl 是针对不同窗口系统的函数。glut是为跨平台的OpenGL程序的工具包,扩展函数库是硬件厂商为实现硬件更新利用OpenGL的扩展机制开发的函数。
GLEW(OpenGL Extension Wrangler Library):是一个专门用于管理OpenGL扩展的C/C++库。在图形编程中,随着时间的推移,OpenGL的功能不断扩展和更新,新的特性和功能以扩展的形式添加到OpenGL中。这些扩展提供了额外的功能,如新的渲染技术、更高效的渲染管线、新的图形效果等。然而,不同的OpenGL实现可能支持不同的扩展,而且开发者需要编写不同的代码来适配不同的扩展,这会增加开发的复杂性。这就是GLEW发挥作用的地方。GLEW简化了使用OpenGL扩展的过程。它提供了一组函数来查询和加载OpenGL的扩展,使得开发者可以轻松地使用最新的功能而不必担心不同平台之间的差异。具体来说,GLEW提供了以下功能:
总的来说,GLEW简化了使用OpenGL扩展的过程,提高了开发效率,同时保证了跨平台的兼容性。开发者可以更加专注于实现图形应用的功能和效果,而无需过多关注底层的扩展加载和管理细节。
GLFW(Graphics Library Framework):的设计目的是为了简化图形应用程序的开发过程,特别是在涉及窗口创建和用户输入处理方面。操作系统的窗口系统通常是复杂且具有差异的,因此直接与之交互会增加开发者的工作量。GLFW通过提供一个统一的接口,为开发者屏蔽了底层操作系统的差异,使得开发图形应用程序变得更加简单和高效,GLFW库提供了以下支持:
所以GLFW是一个功能强大、简单易用且跨平台的图形库,它为开发者提供了一个统一的接口来创建和管理窗口,并处理用户输入,使得图形应用程序的开发变得更加简单和高效。
GLM(OpenGL Mathematics):是一个专门针对OpenGL和图形学的数学库,旨在提供各种数学函数和数据结构,以便于在图形编程中进行数学计算。GLM的设计目标是与OpenGL API兼容,并且提供了类似于GLSL(OpenGL Shading Language)的语法和功能,使得在CPU上进行与GPU相似的数学运算成为可能。GLM库提供以下支持:
所以GLM是一个功能丰富、易于使用且高性能的数学库,为图形编程提供了强大的数学支持。通过与OpenGL API的兼容性和GLSL风格的语法,GLM使得开发者可以轻松地在CPU上进行与GPU相似的数学运算,从而实现复杂的图形效果和计算。
Assimp(Open Asset Import Library):是一个功能强大的库,专门用于导入和导出各种不同格式的3D模型文件。在图形编程中,经常需要处理来自不同来源和不同格式的3D模型,比如OBJ、FBX、Collada等。而Assimp的作用就是为开发者提供一个统一的接口,使得他们可以轻松地导入这些不同格式的模型文件,并且能够方便地在程序中进行处理和使用。Assimp库提供以下支持:
所以Assimp是一个非常实用和强大的库,为开发者处理各种不同格式的3D模型提供了便利和支持。它的统一接口、多种文件格式支持以及额外的功能使得开发者能够更加轻松地导入、处理和使用3D模型文件,从而加速开发过程并提高效率。
GL、GLUT、FreeGLUT和GLAD
GL(Graphics Library):GL是OpenGL的前身,是图形编程中的基础库之一。它提供了一系列基本的图形函数,如绘制点、线、三角形等。在早期的OpenGL版本中,开发者通常会直接使用GL库来进行基本的图形绘制,例如通过调用glBegin()和glEnd()来指定绘制的几何形状,并使用glVertex()来指定顶点坐标。尽管现代OpenGL已经淘汰了这些固定管线的绘制方式,转而采用可编程着色器的方式,但GL仍然作为OpenGL的一部分存在,并且在一些特定的场景下仍然会被使用到。
GLUT(OpenGL Utility Toolkit):GLUT是一个用于创建窗口和处理用户输入的库,旨在简化OpenGL应用程序的开发过程。它提供了一组简单易用的函数,如创建窗口、处理键盘和鼠标输入、管理窗口事件等。通过使用GLUT,开发者可以快速地搭建起一个基本的OpenGL应用程序框架,而无需关注底层的窗口管理和事件处理细节。然而,由于GLUT的功能相对有限,缺乏灵活性,因此在一些复杂的应用场景下可能会显得力不从心。
FreeGLUT:FreeGLUT是GLUT的开源替代品,旨在改进和扩展GLUT的功能,并且保持了兼容性。相比于GLUT,FreeGLUT在功能上进行了一些改进和扩展,如支持多窗口、支持更多的键盘和鼠标事件、提供了更多的定时器函数等。同时,FreeGLUT也修复了一些GLUT中存在的bug和不足之处,使得开发者可以更加灵活地使用和扩展这个库。
GLAD(OpenGL Loader Generator):GLAD是一个用于加载OpenGL函数指针的库,旨在简化跨平台的OpenGL开发过程。在使用OpenGL时,通常需要加载OpenGL的函数指针,以便在运行时调用OpenGL的函数。而GLAD的作用就是自动生成这些函数指针的加载代码,从而使得开发者可以轻松地在不同的平台上使用OpenGL,并且不必担心手动加载函数指针的问题。GLAD可以根据用户指定的OpenGL版本和扩展列表自动生成相应的加载代码,并且支持多种编程语言,如C/C++、Python等,使得开发者可以在不同的开发环境中使用。
这些库之间存在着密切的联系,它们通常一起使用以实现复杂的图形应用程序。例如,一个典型的OpenGL应用可能会使用GLEW来管理OpenGL的扩展,GLFW来创建窗口和处理用户输入,GLM来进行数学计算,以及Assimp来加载和处理3D模型。而在早期的OpenGL开发中,开发者可能会使用GLUT或者FreeGLUT来创建窗口和处理输入。
代码实现
代码1
// 包含标准头文件
#include <stdio.h>
#include <stdlib.h>
// 包含 GLEW 库
#include <GL/glew.h>
// 包含 GLFW 库
#include <GLFW/glfw3.h>
// 包含 GLM 库
#include <glm/glm.hpp>
using namespace glm;
GLFWwindow* window;
int main(void)
{
// 初始化 GLFW
if (!glfwInit())
{
fprintf(stderr, "Failed to initialize GLFW\n");
getchar();
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4); // 设置多重采样抗锯齿
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL 主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // OpenGL 次版本号
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 让 macOS 平台兼容性更好,通常不需要
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式
// 创建窗口及其 OpenGL 上下文
window = glfwCreateWindow(1024, 768, "hello world 01", NULL, NULL);
if (window == NULL) {
fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
getchar();
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// 初始化 GLEW
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
getchar();
glfwTerminate();
return -1;
}
// 确保能够捕获 ESC 键被按下事件
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
// 设置暗蓝色背景
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
do {
// 清空屏幕。
glClear(GL_COLOR_BUFFER_BIT);
// 什么也不绘制
// 交换缓冲区
glfwSwapBuffers(window);
glfwPollEvents();
} // 检查 ESC 键是否被按下或窗口是否被关闭
while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
glfwWindowShouldClose(window) == 0);
// 关闭 OpenGL 窗口并终止 GLFW
glfwTerminate();
return 0;
}
运行结果
代码2
#include <GL/glut.h>
#include <cmath> // 包含cmath头文件
#include <iostream>
float cameraAngleX = 45.0f; // 相机的X轴旋转角度
float cameraAngleY = 45.0f; // 相机的Y轴旋转角度
float cameraDistance = 10.0f; // 相机与原点的距离
int lastMouseX = 0; // 上一次鼠标X位置
int lastMouseY = 0; // 上一次鼠标Y位置
void init() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 设置背景色为黑色
glEnable(GL_DEPTH_TEST); // 启用深度测试
}
void drawGrid() {
// 绘制网格线
glColor3f(1.0f, 1.0f, 1.0f); // 设置颜色为白色
glBegin(GL_LINES);
// 绘制水平线
for (float i = -10.0f; i <= 10.0f; ++i) {
glVertex3f(-10.0f, 0.0f, i);
glVertex3f(10.0f, 0.0f, i);
}
// 绘制垂直线
for (float i = -10.0f; i <= 10.0f; ++i) {
glVertex3f(i, 0.0f, -10.0f);
glVertex3f(i, 0.0f, 10.0f);
}
glEnd();
}
void display() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// 设置相机位置和视角
gluLookAt(cameraDistance * sin(cameraAngleX * 3.14159265f / 180.0f), cameraDistance * sin(cameraAngleY * 3.14159265f / 180.0f), cameraDistance * cos(cameraAngleX * 3.14159265f / 180.0f),
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
drawGrid();
glutSwapBuffers();
}
void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)w / (GLfloat)h, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
lastMouseX = x;
lastMouseY = y;
}
}
void motion(int x, int y) {
float deltaX = x - lastMouseX;
float deltaY = y - lastMouseY;
cameraAngleX += deltaX * 0.1f;
cameraAngleY += deltaY * 0.1f;
lastMouseX = x;
lastMouseY = y;
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y) {
switch (key) {
case 27: // ESC键
exit(0);
break;
case '+': // 放大视角
cameraDistance -= 0.1f;
if (cameraDistance < 1.0f) cameraDistance = 1.0f;
break;
case '-': // 缩小视角
cameraDistance += 0.1f;
if (cameraDistance > 20.0f) cameraDistance = 20.0f;
break;
}
glutPostRedisplay();
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("OpenGL Window");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
运行结果