glut的版本自1998年后就没有更新了,但是其窗口实现相比glfw简便许多。本文主要为文档GLUT » Lighthouse3d.com的学习笔记。
1、环境配置
选择项目-管理NuGet程序包-浏览-搜索freeglut下载即可。FreeGLUT 是 GLUT 的一个开源替代品,旨在提供更多功能和更好的兼容性,它们之间的函数调用和用法基本相同。
头文件为:#include <GL/freeglut.h>,如果为Mac系统,头文件为#include <GLUT/freeglut.h>
2、窗口初始化
glut的窗口初始化相当简单快捷:
#include <GL/freeglut.h>
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
//初始化 GLUT 库
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); //设置显示模式
glutInitWindowPosition(100, 100); //初始化窗口位置(左上角)
glutInitWindowSize(320, 320); //初始化窗口大小
glutCreateWindow("Lighthouse3D- GLUT Tutorial"); //创建窗口
return 1;
}
其中,值得主要的glutInitDisplayMode函数,用于设置窗口显示模式。
用于指定颜色模型的预定义常量包括:
- GLUT_RGBA 或 GLUT_RGB – 选择 RGBA 窗口。这是默认颜色模式。
- GLUT_INDEX – 选择颜色索引模式。
显示模式还允许您选择单缓冲区或双缓冲区窗口。预定义的常量为:
- GLUT_SINGLE – 单个缓冲区窗口
- GLUT_DOUBLE – 双缓冲区窗口,需要有流畅的动画。
还有更多,您可以指定是否需要具有一组特定缓冲区的窗口。最常见的是:
- GLUT_ACCUM – 累积缓冲区
- GLUT_STENCIL – 模具缓冲区
- GLUT_DEPTH – 深度缓冲区
与glfw相同的,这一步创建的窗口没有任何循环,会在创建完成后立刻关闭。
3、窗口循环与三角形绘制示例
void glutDisplayFunc(void (*funcName)(void))注册显示回调函数。告诉 GLUT 在需要重新绘制窗口内容时应该调用哪个函数。
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
void renderScene(void) {
// 设置清除颜色(背景颜色)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 黑色
// 清除颜色缓冲区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES); //绘制三角形
glVertex3f(-0.5, -0.5, 0.0);
glVertex3f(0.5, 0.0, 0.0);
glVertex3f(0.0, 0.5, 0.0);
glEnd();
//glBegin() 与 glEnd() 之间的代码块被称为一个图元的定义块。
// 在这个代码块中,可以通过一系列的顶点坐标来定义一个或多个图元(如点、线、三角形等)。
// 当调用 glEnd() 函数时,表示当前图元的定义结束,OpenGL 将根据之前定义的顶点坐标绘制相应的图元。
glutSwapBuffers(); // 交换前后缓冲区
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(320, 320);
glutCreateWindow("Lighthouse3D - GLUT Tutorial");
glutDisplayFunc(renderScene); //用于注册显示回调函数。告诉 GLUT 在需要重新绘制窗口内容时应该调用哪个函数
glutMainLoop(); //glut窗口循环函数
return 1;
}
绘制结果如下:
4、窗口回调函数
上面形成的三角形会随着窗口的变化而变形。发生这种情况是因为我们没有正确设置透视。默认情况下,透视假定宽度/高度之比为 1,并相应地绘制。因此,当比例改变时,视角就会失真。因此,每次比率变化时,都需要重新计算透视。
窗口回调函数非常通用,这里主要介绍组成其的三个函数:
(1)GLUT 提供了一种方法来定义在调整窗口大小时应调用哪个函数,即注册用于重新计算透视的回调。即glutReshapeFunc 函数。
glutReshapeFunc(void (*func)(int width, int height))
(2)gluPerspective用于设置透视投影矩阵。这个矩阵定义了视锥体的属性,可以确定在三维场景中如何将对象投影到二维视图平面上。
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
示例:gluPerspective(45.0, ratio, 1.0, 100.0);
- 45.0:表示视野角度为 45 度。这个角度决定了视锥体的开口大小,通常 45 度是一个合理的默认值。
- ratio:表示窗口的宽高比。例如,如果窗口的宽度是 800 像素,高度是 600 像素,那么
ratio
应该是 800/600 即 1.333。 - 1.0:表示近裁剪面距离为 1.0 个单位。这意味着任何离摄像机(观察者)距离小于 1.0 的物体都不会被绘制。
- 100.0:表示远裁剪面距离为 100.0 个单位。这意味着任何离摄像机距离大于 100.0 的物体都不会被绘制。
(3)glMatrixMode(GLenum mode)
是一个 OpenGL 函数,用于设置当前的矩阵模式。OpenGL 中有几种不同的矩阵模式,每种模式用于不同的目的,主要有以下三种:
- GL_MODELVIEW:模型视图矩阵模式,这是用于定义对象的变换(如平移、旋转、缩放)和相机的视角变换。
- GL_PROJECTION:投影矩阵模式,这是用于定义投影变换,包括正投影和透视投影。
- GL_TEXTURE:纹理矩阵模式,这是用于对纹理坐标进行变换。
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
void changeSize(int w, int h) { //回调函数,窗口大小改变时调用
// 检查窗口的高度是否为0,以免出现除零错误
if (h == 0)
h = 1;
float ratio = w * 1.0 / h;
// 切换到投影矩阵模式
glMatrixMode(GL_PROJECTION);
// 将当前矩阵重置为单位矩阵
glLoadIdentity();
// 将视口设置为整个窗口的大小
glViewport(0, 0, w, h);
// 设置正确的透视投影
gluPerspective(45, ratio, 1, 100);
//切换回模型视图矩阵模式
glMatrixMode(GL_MODELVIEW);
}
void renderScene(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glVertex3f(-2, -2, -5.0);
glVertex3f(2, 0.0, -5.0);
glVertex3f(0.0, 2, -5.0);
glEnd();
glutSwapBuffers();
}
int main(int argc, char** argv) {
// init GLUT and create window
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(320, 320);
glutCreateWindow("Lighthouse3D - GLUT Tutorial");
// register callbacks
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
// enter GLUT event processing loop
glutMainLoop();
return 1;
}