/*
* 完整的GLFW应用程序示例
* 包含窗口创建、输入处理、渲染循环等所有主要功能
* 每行代码都有详细注释,便于学习GLFW的使用
*/
#include <GLFW/glfw3.h> // GLFW主头文件
#include <iostream> // 用于控制台输出
#include <cmath> // 用于数学计算
// 全局变量定义
GLFWwindow* g_window = nullptr; // 全局窗口指针
int g_windowWidth = 800; // 窗口宽度
int g_windowHeight = 600; // 窗口高度
float g_backgroundColor[3] = {0.2f, 0.3f, 0.3f}; // 背景颜色(RGB)
bool g_wireframeMode = false; // 线框模式标志
/*
* 错误回调函数
* 当GLFW发生错误时自动调用
*/
void errorCallback(int error, const char* description) {
std::cerr << "GLFW错误 " << error << ": " << description << std::endl;
}
/*
* 窗口大小改变回调函数
* 当用户调整窗口大小时自动调用
*/
void framebufferSizeCallback(GLFWwindow* window, int width, int height) {
g_windowWidth = width;
g_windowHeight = height;
// 设置视口大小,匹配窗口大小
glViewport(0, 0, width, height);
std::cout << "窗口大小改变: " << width << " x " << height << std::endl;
}
/*
* 键盘按键回调函数
* 当用户按下或释放键盘按键时调用
*/
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
// 只处理按键按下事件,忽略重复和释放事件
if (action == GLFW_PRESS) {
switch (key) {
case GLFW_KEY_ESCAPE:
// ESC键:关闭窗口
std::cout << "ESC键按下,关闭窗口" << std::endl;
glfwSetWindowShouldClose(window, true);
break;
case GLFW_KEY_SPACE:
// 空格键:切换背景颜色
g_backgroundColor[0] = static_cast<float>(rand()) / RAND_MAX;
g_backgroundColor[1] = static_cast<float>(rand()) / RAND_MAX;
g_backgroundColor[2] = static_cast<float>(rand()) / RAND_MAX;
std::cout << "空格键按下,切换背景颜色" << std::endl;
break;
case GLFW_KEY_W:
// W键:切换线框模式
g_wireframeMode = !g_wireframeMode;
if (g_wireframeMode) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
std::cout << "线框模式: 开启" << std::endl;
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
std::cout << "线框模式: 关闭" << std::endl;
}
break;
case GLFW_KEY_F:
// F键:切换全屏/窗口模式
static bool fullscreen = false;
fullscreen = !fullscreen;
if (fullscreen) {
// 获取主显示器模式
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
// 切换到全屏模式
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
std::cout << "切换到全屏模式" << std::endl;
} else {
// 切换到窗口模式
glfwSetWindowMonitor(window, nullptr, 100, 100, g_windowWidth, g_windowHeight, 0);
std::cout << "切换到窗口模式" << std::endl;
}
break;
case GLFW_KEY_P:
// P键:打印窗口信息
int x, y;
glfwGetWindowPos(window, &x, &y);
std::cout << "窗口位置: (" << x << ", " << y << ")" << std::endl;
std::cout << "窗口大小: " << g_windowWidth << " x " << g_windowHeight << std::endl;
break;
}
}
}
/*
* 鼠标移动回调函数
* 当鼠标移动时调用
*/
void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
// 可以在这里处理鼠标移动逻辑
// 示例:每100次调用打印一次位置(避免过于频繁的输出)
static int callCount = 0;
if (callCount++ % 100 == 0) {
std::cout << "鼠标位置: (" << xpos << ", " << ypos << ")" << std::endl;
}
}
/*
* 鼠标按钮回调函数
* 当鼠标按钮按下或释放时调用
*/
void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
if (action == GLFW_PRESS) {
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
switch (button) {
case GLFW_MOUSE_BUTTON_LEFT:
std::cout << "左键点击位置: (" << xpos << ", " << ypos << ")" << std::endl;
break;
case GLFW_MOUSE_BUTTON_RIGHT:
std::cout << "右键点击位置: (" << xpos << ", " << ypos << ")" << std::endl;
break;
case GLFW_MOUSE_BUTTON_MIDDLE:
std::cout << "中键点击位置: (" << xpos << ", " << ypos << ")" << std::endl;
break;
}
}
}
/*
* 鼠标滚轮回调函数
* 当鼠标滚轮滚动时调用
*/
void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
std::cout << "鼠标滚轮: X=" << xoffset << ", Y=" << yoffset << std::endl;
}
/*
* 窗口焦点回调函数
* 当窗口获得或失去焦点时调用
*/
void windowFocusCallback(GLFWwindow* window, int focused) {
if (focused) {
std::cout << "窗口获得焦点" << std::endl;
} else {
std::cout << "窗口失去焦点" << std::endl;
}
}
/*
* 窗口图标化回调函数
* 当窗口被最小化或恢复时调用
*/
void windowIconifyCallback(GLFWwindow* window, int iconified) {
if (iconified) {
std::cout << "窗口被最小化" << std::endl;
} else {
std::cout << "窗口恢复" << std::endl;
}
}
/*
* 初始化GLFW和创建窗口
* 返回true表示成功,false表示失败
*/
bool initializeGLFW() {
// 设置错误回调函数
glfwSetErrorCallback(errorCallback);
// 初始化GLFW库
if (!glfwInit()) {
std::cerr << "GLFW初始化失败!" << std::endl;
return false;
}
// 配置GLFW窗口属性
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // OpenGL次版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // 窗口可调整大小
glfwWindowHint(GLFW_SAMPLES, 4); // 4倍多重采样抗锯齿
#ifdef __APPLE__
// macOS需要额外的配置
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// 创建窗口和OpenGL上下文
g_window = glfwCreateWindow(g_windowWidth, g_windowHeight, "GLFW完整示例 - 学习OpenGL", nullptr, nullptr);
if (!g_window) {
std::cerr << "创建GLFW窗口失败!" << std::endl;
glfwTerminate();
return false;
}
// 将窗口的上下文设置为当前线程的主上下文
glfwMakeContextCurrent(g_window);
// 设置垂直同步(1表示开启,0表示关闭)
glfwSwapInterval(1);
// 设置各种回调函数
glfwSetFramebufferSizeCallback(g_window, framebufferSizeCallback);
glfwSetKeyCallback(g_window, keyCallback);
glfwSetCursorPosCallback(g_window, cursorPosCallback);
glfwSetMouseButtonCallback(g_window, mouseButtonCallback);
glfwSetScrollCallback(g_window, scrollCallback);
glfwSetWindowFocusCallback(g_window, windowFocusCallback);
glfwSetWindowIconifyCallback(g_window, windowIconifyCallback);
// 设置输入模式
glfwSetInputMode(g_window, GLFW_STICKY_KEYS, GLFW_TRUE); // 启用粘性键
glfwSetInputMode(g_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // 正常光标模式
std::cout << "GLFW初始化成功!" << std::endl;
std::cout << "控制说明:" << std::endl;
std::cout << " ESC - 退出程序" << std::endl;
std::cout << " 空格 - 随机改变背景颜色" << std::endl;
std::cout << " W - 切换线框模式" << std::endl;
std::cout << " F - 切换全屏/窗口模式" << std::endl;
std::cout << " P - 打印窗口信息" << std::endl;
return true;
}
/*
* 渲染一个简单的旋转三角形
*/
void renderTriangle(float time) {
// 开始绘制三角形
glBegin(GL_TRIANGLES);
// 计算旋转角度(随时间变化)
float angle = time * 50.0f; // 每秒旋转50度
// 第一个顶点(红色)
glColor3f(1.0f, 0.0f, 0.0f); // 红色
glVertex2f(0.0f * cos(angle) - 0.5f * sin(angle),
0.0f * sin(angle) + 0.5f * cos(angle));
// 第二个顶点(绿色)
glColor3f(0.0f, 1.0f, 0.0f); // 绿色
glVertex2f(-0.5f * cos(angle) - (-0.5f) * sin(angle),
-0.5f * sin(angle) + (-0.5f) * cos(angle));
// 第三个顶点(蓝色)
glColor3f(0.0f, 0.0f, 1.0f); // 蓝色
glVertex2f(0.5f * cos(angle) - (-0.5f) * sin(angle),
0.5f * sin(angle) + (-0.5f) * cos(angle));
glEnd();
}
/*
* 渲染场景
*/
void renderScene(float time) {
// 清除颜色缓冲区
glClear(GL_COLOR_BUFFER_BIT);
// 设置投影矩阵为正交投影
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
// 设置模型视图矩阵
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// 渲染旋转三角形
renderTriangle(time);
}
/*
* 主函数 - 程序入口点
*/
int main() {
std::cout << "启动GLFW应用程序..." << std::endl;
// 初始化GLFW
if (!initializeGLFW()) {
return -1;
}
// 设置初始视口
glViewport(0, 0, g_windowWidth, g_windowHeight);
// 获取时间基准
double lastTime = glfwGetTime();
int frameCount = 0;
// 主渲染循环
while (!glfwWindowShouldClose(g_window)) {
// 计算帧率
double currentTime = glfwGetTime();
frameCount++;
// 每秒更新一次帧率显示
if (currentTime - lastTime >= 1.0) {
std::string title = "GLFW示例 - FPS: " + std::to_string(frameCount);
glfwSetWindowTitle(g_window, title.c_str());
frameCount = 0;
lastTime = currentTime;
}
// 设置清除颜色(背景色)
glClearColor(g_backgroundColor[0], g_backgroundColor[1], g_backgroundColor[2], 1.0f);
// 渲染场景
renderScene(static_cast<float>(currentTime));
// 交换前后缓冲区(双缓冲技术)
glfwSwapBuffers(g_window);
// 处理事件(输入、窗口事件等)
glfwPollEvents();
// 检查某些特定按键状态(持续按下的处理)
if (glfwGetKey(g_window, GLFW_KEY_R) == GLFW_PRESS) {
// R键持续按下时重置背景颜色
g_backgroundColor[0] = 0.2f;
g_backgroundColor[1] = 0.3f;
g_backgroundColor[2] = 0.3f;
}
}
// 清理资源
glfwDestroyWindow(g_window);
glfwTerminate();
std::cout << "程序正常退出" << std::endl;
return 0;
}