You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:57:30