【问题标题】:How to draw while resizing GLFW window?如何在调整 GLFW 窗口大小时进行绘制?
【发布时间】:2018-02-03 10:27:02
【问题描述】:

每当我调整 GLFW 窗口的大小时,它不会在我调整窗口大小时绘制。窗口新暴露的部分只有在我完成窗口大小调整后才会被绘制。您可以在下面的图片中自己看到它:

这是我的应用程序的代码。我在 Visual Studio 2015 上运行 Windows 10

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void get_resolution(int* window_width, int* window_height);
void initGlfwSettings();
GLFWwindow* initGlfwWindow();
void initGlad();

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    initGlfwSettings();

    GLFWwindow* window = initGlfwWindow();

    initGlad();

    // glad: load all OpenGL function pointers
    // ---------------------------------------


    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        int width, height;
        glfwGetWindowSize(window, &width, &height);


        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
        // input
        // -----
        processInput(window);

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

void get_resolution(int* window_width, int* window_height) {
    const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());

    *window_width = mode->width;
    *window_height = mode->height;
}

void initGlfwSettings()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);


    #ifdef __APPLE__
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
    #endif
}

GLFWwindow* initGlfwWindow()
{
    /*GLFWmonitor* monitor = glfwGetPrimaryMonitor();
    int width;
    int height;

    get_resolution(&width, &height);*/



    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "learning opengl", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        exit(1);
    }

    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSwapInterval(1);

    return window;
}

void initGlad()
{
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        exit(1);
    }
}

解释这个问题的任何解决方案。

【问题讨论】:

    标签: c++ opengl glfw window-resize


    【解决方案1】:

    每当调整窗口大小时,事件处理 (glfwPollEvents) 就会停止,但在此过程中,它会不断发出调整大小事件,这些事件由您已经使用的调整大小回调动态处理。即使在glfwPollEvents 中,您也可以从那里重新绘制场景并调用glfwSwapBuffers 进行渲染。实际上,这可以通过以下代码来实现:

    void draw()
    {
        // Rendering code goes here
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
    }
    
    void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    {
        // make sure the viewport matches the new window dimensions; note that width and 
        // height will be significantly larger than specified on retina displays.
        glViewport(0, 0, width, height);
        // Re-render the scene because the current frame was drawn for the old resolution
        draw();
    }
    

    并将您的渲染(glClearColorglClearglfwSwapBuffers)从主循环移动到 draw。在主循环中留下对draw 的调用,并将window 设为全局变量或在调用时将其传递给draw,否则它将超出draw 的范围,而glfwSwapBuffers 需要它.

    仅当用户在按住鼠标的同时移动鼠标时才有效 - 仅在调整大小窗口部分按住左键单击仍会停止。要解决这个问题,您需要在除此之外的单独线程中进行渲染。(不,没有线程您无法做到这一点。抱歉,这就是 GLFW 的工作方式,除了他们之外没有人可以改变它。)

    【讨论】:

    • "要解决这个问题,除此之外,您还需要在单独的线程中进行渲染。"这是否意味着使 GL 上下文处于当前状态,然后 每次 在任一线程上绘制时将其分离?否则,如何在渲染循环和调整大小回调中进行绘制?
    • 从一点测试来看,resize回调最好从渲染循环请求一个ctx切换,这样渲染循环就不用担心“正常”下的ctx切换了操作(即不调整大小)。此解决方案可能会在调整大小期间消耗大量 CPU 周期,但是……谁在乎呢。调整大小不是正常操作,此解决方案可提供完美平滑的大小调整,而无需担心 SIZEMOVE 循环的特殊性。
    【解决方案2】:

    这是 Windows 事件处理程序没有将控制权返回给主线程。它是 Windows 的工作方式,您无法更改它。

    但是,您可以将所有渲染和 glfw 命令移动到不同的线程,这不会被 windows 停止。

    【讨论】:

    • 这通常是一件好事,控件仅在调整大小完成后才返回,因为您经常有与窗口大小相同的帧缓冲区。调整大小完成后,它们只需要调整一次。
    • 我只想不断更新我构建的所有内容。诸如运行视频或实时数据之类的事情,至少捕获需要自己的线程来避免停顿。
    • @JonathanOlson 您将如何实现这一点?我尝试了两次自己实现它,一旦显示所有内容,但始终会显示加载光标。在我的第二次尝试显示黑屏。
    • 这里有一个简短的讨论可能会有所帮助。 sourceforge.net/p/glfw/discussion/247562/thread/2e6e361e
    • @JonathanOlson 我使用 std::thread 将他的代码转换为代码。它显示一个三角形,但调整大小时窗口不会更新。源码here
    【解决方案3】:

    您不需要设置单独的线程。

    您只需要处理 WM_SIZE 消息并在它发生时重新绘制,可以使用 glfwSetWindowSizeCallback 函数访问。您可以调用循环内的所有内容,或者只调用渲染和交换缓冲区部分。

    在进行拖动操作时,Windows 不会从消息处理中返回,但会继续处理事件。因此,您只需要在回调中的每一帧都做任何您想做的事情,再加上足以重新配置您的视口和矩阵以及任何新客户端大小的东西。这个原则也适用于窗口移动或类似的事情。

    对于 Win32 或 glfw,基本流程是:

    void MyStart()
    {
      for glfw set the callback to MyCallback via glfwSetWindowSizeCallback
    
      while(!ShouldExit)
      {
        some code
        MyFrame()
      }
    }
    
    void MyFrame()
    {
      do things I normally do per frame
    
      paint this and that
    
      swap buffers
    }
    
    void MyCallback()
    { 
        get new client area size
        reset viewport or fov
        MyFrame();   
    }
    
    // for windows without glfw
    void WndProc()
    {
      WM_SIZE:
        MyCallback();
    }
    

    【讨论】:

    • Still 在调整大小期间鼠标不移动时停止更新/绘图,至少在带有 GLFW 3.2.1 的 Windows 7 上。
    • @genpfault 检查回调是否在您拖动调整大小手柄时实际触发。其余的应该只是绘图。看看这是否有帮助:forum.lwjgl.org/index.php?topic=6090.0
    猜你喜欢
    • 1970-01-01
    • 2011-09-29
    • 1970-01-01
    • 2017-09-25
    • 2019-06-11
    • 1970-01-01
    • 1970-01-01
    • 2015-01-28
    • 2014-01-01
    相关资源
    最近更新 更多