c++ - 在 OpenGL/GLFW 3.2 中在窗口和全屏之间切换

标签 c++ opengl glfw

我正在学习 Linux 上的 OpenGL,但我无法进行模式切换(窗口化到全屏再返回)。

窗口似乎进入全屏模式,但看起来不正确。要切换模式,将创建一个新窗口并销毁旧窗口。

void OpenGLWindow::FullScreen(bool fullScreen, int width, int height)
{
    GLFWwindow *oldHandle = m_window;

    m_fullscreen = fullScreen;
    m_width = width;
    m_height = height;

    m_window = glfwCreateWindow(width, height, m_caption.c_str(),
        fullScreen ? m_monitor : NULL, m_window);

    if (m_window == NULL)
    {
        glfwTerminate();
        throw std::runtime_error("Failed to recreate window.");
    }

    glfwDestroyWindow(oldHandle);

    m_camera->Invalidate();

    // Use entire window for rendering.
    glViewport(0, 0, width, height);

    glfwMakeContextCurrent(m_window);
    glfwSwapInterval(1);

    if (m_keyboardHandler) SetKeyboardHandler(m_keyboardHandler);
}

初始窗口 enter image description here

全屏(不正确) enter image description here

返回窗口化 enter image description here

问题更新

我已更新代码以使用您的代码并遇到相同的问题。根据您的建议,我现在正在更新相机,但仍然无济于事:(

void OpenGLCamera::Invalidate()
{
    RecalculateProjection(m_perspProjInfo->Width(), m_perspProjInfo->Height());
    m_recalculateViewMatrix = true;
    m_recalculatePerspectiveMatrix = true;
    m_recalculateProjectionMatrix = true;
}

void OpenGLCamera::RecalculateProjection(int width, int height)
{
    float aspectRatio = float(width) / height;
    float frustumYScale = cotangent(degreesToRadians(
        m_perspProjInfo->FieldOfView() / 2));

    float frustumXScale = frustumYScale;

    if (width > height) 
    {
        // Shrink the x scale in eye-coordinate space, so that when geometry is
        // projected to ndc-space, it is widened out to become square.
        m_projectionMatrix[0][0] = frustumXScale / aspectRatio;
        m_projectionMatrix[1][1] = frustumYScale;
    }
    else {
        // Shrink the y scale in eye-coordinate space, so that when geometry is
        // projected to ndc-space, it is widened out to become square.
        m_projectionMatrix[0][0] = frustumXScale;
        m_projectionMatrix[1][1] = frustumYScale * aspectRatio;
    }
}

兔子:当我调整大小时:

enter image description here

兔子:当我进入全屏时: enter image description here

最佳答案

在下文中,我将描述一个小而方便的类,它处理调整 GLFW 窗口的大小并处理打开和关闭全屏窗口。
GLFW documentation 中详细记录了所有使用的 GLFW 函数.

#include <GL/gl.h>
#include <GLFW/glfw3.h>
#include <array>
#include <stdexcept>

class OpenGLWindow
{
private:

    std::array< int, 2 > _wndPos         {0, 0};
    std::array< int, 2 > _wndSize        {0, 0};
    std::array< int, 2 > _vpSize         {0, 0};
    bool                 _updateViewport = true;
    GLFWwindow *         _wnd            = nullptr;
    GLFWmonitor *        _monitor        = nullptr;

    void Resize( int cx, int cy );

public:

    void Init( int width, int height );
    static void CallbackResize(GLFWwindow* window, int cx, int cy);
    void MainLoop ( void );
    bool IsFullscreen( void );
    void SetFullScreen( bool fullscreen );
};

当创建窗口时,用户函数指针(glfwSetWindowUserPointer)被设置为窗口管理类。调整大小回调由 glfwSetWindowSizeCallback 设置。创建窗口后,可以通过 glfwGetWindowPosglfwGetWindowSize 获取其当前大小和位置。

void OpenGLWindow::Init( int width, int height )
{
    _wnd = glfwCreateWindow( width, height, "OGL window", nullptr, nullptr );
    if ( _wnd == nullptr )
    {
        glfwTerminate();
        throw std::runtime_error( "error initializing window" ); 
    }

    glfwMakeContextCurrent( _wnd );

    glfwSetWindowUserPointer( _wnd, this );
    glfwSetWindowSizeCallback( _wnd, OpenGLWindow::CallbackResize );

    _monitor =  glfwGetPrimaryMonitor();
    glfwGetWindowSize( _wnd, &_wndSize[0], &_wndSize[1] );
    glfwGetWindowPos( _wnd, &_wndPos[0], &_wndPos[1] );
    _updateViewport = true;
}

当调整大小通知发生时,可以通过glfwGetWindowUserPointer获取窗口管理类的指针:

static void OpenGLWindow::CallbackResize(GLFWwindow* window, int cx, int cy)
{
    void *ptr = glfwGetWindowUserPointer( window );
    if ( OpenGLWindow *wndPtr = static_cast<OpenGLWindow*>( ptr ) )
        wndPtr->Resize( cx, cy );
}

通知窗口大小的任何更改并存储新的窗口大小(glfwGetWindowSize):

void OpenGLWindow::Resize( int cx, int cy )
{
    _updateViewport = true;
}

当窗口大小发生变化时,视口(viewport)必须适合窗口大小 ( glViewport )。这可以在应用程序的主循环中完成:

void OpenGLWindow::MainLoop ( void )
{
    while (!glfwWindowShouldClose(_wnd))
    {
        if ( _updateViewport )
        {
            glfwGetFramebufferSize( _wnd, &_vpSize[0], &_vpSize[1] );
            glViewport( 0, 0, _vpSize[0], _vpSize[1] );
            _updateViewport = false;
        }

        // ..... render the scene

        glfwSwapBuffers(_wnd);
        glfwPollEvents();
    }
}  

如果当前窗口处于全屏模式,可以通过请求窗口用于全屏模式的监视器(glfwGetWindowMonitor)来实现:

bool OpenGLWindow::IsFullscreen( void )
{
    return glfwGetWindowMonitor( _wnd ) != nullptr;
} 

要打开和关闭全屏模式,必须调用 glfwSetWindowMonitor,可以使用全屏模式的监视器,也可以使用 nullptr:

void OpenGLWindow::SetFullScreen( bool fullscreen )
{
    if ( IsFullscreen() == fullscreen )
        return;

    if ( fullscreen )
    {
        // backup window position and window size
        glfwGetWindowPos( _wnd, &_wndPos[0], &_wndPos[1] );
        glfwGetWindowSize( _wnd, &_wndSize[0], &_wndSize[1] );
        
        // get resolution of monitor
        const GLFWvidmode * mode = glfwGetVideoMode(_monitor);

        // switch to full screen
        glfwSetWindowMonitor( _wnd, _monitor, 0, 0, mode->width, mode->height, 0 );
    }
    else
    {
        // restore last window size and position
        glfwSetWindowMonitor( _wnd, nullptr,  _wndPos[0], _wndPos[1], _wndSize[0], _wndSize[1], 0 );
    }

    _updateViewport = true;
}

关于c++ - 在 OpenGL/GLFW 3.2 中在窗口和全屏之间切换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47402766/

相关文章:

c++ - Linux网络编程。我可以从什么开始?

c++ - OpenGL glGenVertexArray() 异常

java - glfwGetWindowSize 结果为 SIGSEGV (LWJGL)

c++ - pugiXML 定位具有相同 parent 值的 child

c++ - 为什么 std::vector<std::string> 中的字符串最终具有相同的数据地址?

java - OpenGL 版本

c++ - 从 Freeglut/Opengl 2.1 项目获取返回值

c++ - Cmake vs 命令行 : Error in Cmake, 不在命令行

c++ - OpenGL 程序不断崩溃?

c++ - 用于编辑文本的最小 CPU 和内存开销数据结构?