web-dev-qa-db-ja.com

OpenGL / GLFW3.2でウィンドウ表示と全画面表示を切り替える

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

7
Paul Morriss

以下では、GLFWウィンドウのサイズ変更を扱い、フルスクリーンウィンドウのオンとオフの切り替えを処理する、小​​さいながらも便利なクラスについて説明します。
使用されているすべてのGLFW関数は、 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;
}

ウィンドウサイズが変更された場合、ビューポートはウィンドウサイズに適合している必要があります( 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 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(glfwGetPrimaryMonitor());

        // 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;
}
10
Rabbid76

ウィンドウとフルスクリーンを切り替えるだけの場合は、glfwCreateWindowで新しいウィンドウを作成しないことをお勧めします。代わりにglfwSetWindowMonitorを使用してください。

フルスクリーンを有効にしてウィンドウを作成する場合は、モニターのビデオモードと互換性のある引数を渡す必要があります。次のように、プライマリモニターで標準のビデオモードを取得できます。

GLFWmonitor *monitor = glfwGetPrimaryMonitor();
const GLFWvidmode *mode = glfwGetVideoMode(monitor);

フルスクリーンに切り替えるには:

glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);

もちろん、nullptr- modeと独自の値を渡すだけです。

glfwSetWindowMonitor(window, nullptr, 0, 0, windowWidth, windowHeight, windowRefreshRate);

また、ビューポートのサイズを変更してカメラを更新することを忘れないでください。


ユーザーがウィンドウのサイズを変更したときに、ビューポートのサイズを変更し、カメラを更新していますか?

3
ShroomPow

コードにはいくつかの問題があります。

  1. フルスクリーンモードでglfwCreateWindowが解像度をwidth * heightに設定すると仮定するのは正しくありません。 GLFWドキュメント 状態(私の強調):

    フルスクリーンウィンドウの場合、指定されたサイズがウィンドウの目的のビデオモードの解像度になります。フルスクリーンウィンドウがアイコン化されていない限り、サポートされているビデオモードが目的のビデオモードに最も近いものが指定されたモニターに設定されます。

  2. ウィンドウサイズが「ピクセル」で指定されていると仮定しても正しくありません。 ドキュメントの関連部分 をもう一度引用します。

    ウィンドウのサイズは画面座標で測定されますが、OpenGLはピクセルで機能します。たとえば、glViewportに渡すサイズはピクセル単位である必要があります。一部のマシンでは画面の座標とピクセルが同じですが、他のマシンでは同じではありません。ウィンドウのフレームバッファのサイズ(ピクセル単位)を取得するための2番目の関数セットがあります。

問題1と2は、ウィンドウの作成後にglfwGetFramebufferSizeを呼び出すだけで解決できます。これにより、問題3が残ります。

  1. 現在のGLコンテキストを持たずにglViewportを呼び出すと、未定義の動作が発生し、特にビューポートがまったく設定されなくなります。これは実際には興味深いものです。新しいコンテキストのビューポートは完全に新しいウィンドウになるため、間違い1と2は直接的な影響はありません。コードが、有用な値を含むm_widthm_heightに依存している場合は、後で何らかの影響が生じる可能性があります。 、しかし。
2
derhass