在OpenGL中能否不依赖GLUT实现窗口大小调整功能?
不依赖GLUT实现OpenGL窗口大小调整的方法
当然有啦!GLUT虽然是早年OpenGL项目里常用的工具,但现在有很多更灵活的替代方案,甚至可以直接用原生窗口API来处理窗口大小调整。下面给你几种实用的实现方式:
使用GLFW(推荐)
GLFW是轻量跨平台的窗口与输入处理库,比GLUT更现代、更灵活,是目前OpenGL项目的主流选择之一。处理窗口大小调整的逻辑非常清晰:
- 初始化GLFW并创建可调整大小的窗口
- 绑定窗口大小回调函数,在回调中更新视口与投影矩阵
#include <GLFW/glfw3.h> // 窗口大小调整回调函数 void framebuffer_size_callback(GLFWwindow* window, int width, int height) { // 避免高度为0导致的除法错误 if (height == 0) height = 1; float aspect_ratio = static_cast<float>(width) / height; // 设置视口匹配窗口最新尺寸 glViewport(0, 0, width, height); // 更新透视投影矩阵,保证画面比例正确 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, aspect_ratio, 0.1f, 100.0f); glMatrixMode(GL_MODELVIEW); } int main() { // 初始化GLFW if (!glfwInit()) return -1; // 创建可调整大小的窗口 GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Window", NULL, NULL); if (!window) { glfwTerminate(); return -1; } // 将窗口设为当前OpenGL上下文 glfwMakeContextCurrent(window); // 绑定窗口大小回调 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 初始调用一次,确保启动时视口正确 int width, height; glfwGetFramebufferSize(window, &width, &height); framebuffer_size_callback(window, width, height); // 主渲染循环 while (!glfwWindowShouldClose(window)) { // 清空颜色缓冲区 glClear(GL_COLOR_BUFFER_BIT); // 这里添加你的渲染逻辑... // 交换前后缓冲区并处理事件 glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0; }
使用SDL
SDL是另一个跨平台多媒体库,除了窗口管理,还支持音频、输入等功能,适合需要更多多媒体能力的项目:
#include <SDL2/SDL.h> #include <SDL2/SDL_opengl.h> int main(int argc, char* argv[]) { // 初始化SDL视频模块 if (SDL_Init(SDL_INIT_VIDEO) != 0) return -1; // 设置OpenGL版本属性 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); // 创建可调整大小的窗口 SDL_Window* window = SDL_CreateWindow( "OpenGL Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE ); SDL_GLContext glContext = SDL_GL_CreateContext(window); int width = 800, height = 600; // 初始视口设置 glViewport(0, 0, width, height); SDL_Event event; bool running = true; while (running) { // 处理事件队列 while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { running = false; } // 监听窗口大小变化事件 else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) { width = event.window.data1; height = event.window.data2; if (height == 0) height = 1; float aspect_ratio = static_cast<float>(width) / height; // 更新视口与投影矩阵 glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, aspect_ratio, 0.1f, 100.0f); glMatrixMode(GL_MODELVIEW); } } // 清空颜色缓冲区 glClear(GL_COLOR_BUFFER_BIT); // 这里添加你的渲染逻辑... SDL_GL_SwapWindow(window); } // 资源清理 SDL_GL_DeleteContext(glContext); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
原生窗口API(以Windows为例)
如果不想依赖任何第三方库,可以直接用Win32原生API实现,核心是监听WM_SIZE消息:
#include <windows.h> #include <gl/gl.h> #include <gl/glu.h> LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { const wchar_t CLASS_NAME[] = L"OpenGL Window Class"; // 注册窗口类 WNDCLASS wc = {0}; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; wc.hCursor = LoadCursor(NULL, IDC_ARROW); RegisterClass(&wc); // 创建可调整大小的窗口 HWND hwnd = CreateWindowEx( 0, CLASS_NAME, L"OpenGL Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL ); if (!hwnd) return 0; // 设置OpenGL像素格式 PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0,0,0,0,0,0, 0,0,0,0,0,0,0, 32, 0,0, PFD_MAIN_PLANE, 0,0,0,0 }; HDC hdc = GetDC(hwnd); int pixelFormat = ChoosePixelFormat(hdc, &pfd); SetPixelFormat(hdc, pixelFormat, &pfd); HGLRC hglrc = wglCreateContext(hdc); wglMakeCurrent(hdc, hglrc); ShowWindow(hwnd, nCmdShow); // 消息循环 MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 资源清理 wglDeleteContext(hglrc); ReleaseDC(hwnd, hdc); return static_cast<int>(msg.wParam); } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { // 处理窗口大小变化消息 case WM_SIZE: { int width = LOWORD(lParam); int height = HIWORD(lParam); if (height == 0) height = 1; float aspect_ratio = static_cast<float>(width) / height; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, aspect_ratio, 0.1f, 100.0f); glMatrixMode(GL_MODELVIEW); // 触发窗口重绘 InvalidateRect(hwnd, NULL, FALSE); break; } // 处理窗口绘制消息 case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); glClear(GL_COLOR_BUFFER_BIT); // 这里添加你的渲染逻辑... SwapBuffers(hdc); EndPaint(hwnd, &ps); break; } case WM_DESTROY: { PostQuitMessage(0); break; } default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; }
核心逻辑总结
不管用哪种方案,核心步骤都是一致的:
- 监听窗口大小变化事件
- 获取新的窗口宽高(注意避免高度为0的情况)
- 调用
glViewport更新视口,匹配窗口新尺寸 - 调整投影矩阵(透视或正交)以适配新的宽高比,保证画面不会拉伸变形
内容的提问来源于stack exchange,提问作者Ziming Wang
相关产品推荐
相关产品推荐

