Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rlgl] BeginTextureMode() doesn't update intenal framebuffer size for rendering in VR #2420

Closed
FireFlyForLife opened this issue Mar 30, 2022 · 6 comments

Comments

@FireFlyForLife
Copy link
Contributor

Issue description

I'm building a OpenXR binding for Raylib. The issue I have is that rlEnableFramebuffer() or BeginTextureMode() don't update the framebuffer size in RLGL (though will set CORE.Window.currentFbo width/height in Raylib rcore.c).

The problem is that while rendering normally, BeginTextureMode() will call rlViewport() to setup the new size. But in RLGL's rlDrawRenderBatch() function, the VR codepath will overwrite the viewport size to each eye from the default screen size, instead of the enabled framebuffer and render texture.

rlgl.h@L2505:

if (eyeCount == 2)
{
    // Setup current eye viewport (half screen width)
    rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight);
    
    // ...

Environment

Windows 10,
Both Opengl 3.3 & 4.3,
NVidia GTX 980.

Issue Screenshot

NVIDIA_Share_zYm43nVmZ9

Code Example

I'm obviously using OpenXR instead of the simulator. But taking the vr simulator example and modifying the render target resolution will reproduce the issue too:

/*******************************************************************************************
*
*   raylib [core] example - VR Simulator (Oculus Rift CV1 parameters)
*
*   This example has been created using raylib 3.7 (www.raylib.com)
*   raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
*   Copyright (c) 2017-2021 Ramon Santamaria (@raysan5)
*
********************************************************************************************/

#include "raylib.h"

#if defined(PLATFORM_DESKTOP)
    #define GLSL_VERSION        330
#else   // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
    #define GLSL_VERSION        100
#endif

int main(void)
{
    // Initialization
    //--------------------------------------------------------------------------------------
    const int screenWidth = 800;
    const int screenHeight = 450;

    // NOTE: screenWidth/screenHeight should match VR device aspect ratio
    InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator");

    // VR device parameters definition
    VrDeviceInfo device = {
        // Oculus Rift CV1 parameters for simulator
        .hResolution = 2160,                 // Horizontal resolution in pixels
        .vResolution = 1200,                 // Vertical resolution in pixels
        .hScreenSize = 0.133793f,            // Horizontal size in meters
        .vScreenSize = 0.0669f,              // Vertical size in meters
        .vScreenCenter = 0.04678f,           // Screen center in meters
        .eyeToScreenDistance = 0.041f,       // Distance between eye and display in meters
        .lensSeparationDistance = 0.07f,     // Lens separation distance in meters
        .interpupillaryDistance = 0.07f,     // IPD (distance between pupils) in meters

        // NOTE: CV1 uses fresnel-hybrid-asymmetric lenses with specific compute shaders
        // Following parameters are just an approximation to CV1 distortion stereo rendering
        .lensDistortionValues[0] = 1.0f,     // Lens distortion constant parameter 0
        .lensDistortionValues[1] = 0.22f,    // Lens distortion constant parameter 1
        .lensDistortionValues[2] = 0.24f,    // Lens distortion constant parameter 2
        .lensDistortionValues[3] = 0.0f,     // Lens distortion constant parameter 3
        .chromaAbCorrection[0] = 0.996f,     // Chromatic aberration correction parameter 0
        .chromaAbCorrection[1] = -0.004f,    // Chromatic aberration correction parameter 1
        .chromaAbCorrection[2] = 1.014f,     // Chromatic aberration correction parameter 2
        .chromaAbCorrection[3] = 0.0f,       // Chromatic aberration correction parameter 3
    };

    // Load VR stereo config for VR device parameteres (Oculus Rift CV1 parameters)
    VrStereoConfig config = LoadVrStereoConfig(device);

    // Distortion shader (uses device lens distortion and chroma)
    Shader distortion = LoadShader(0, TextFormat("resources/distortion%i.fs", GLSL_VERSION));

    // Update distortion shader with lens and distortion-scale parameters
    SetShaderValue(distortion, GetShaderLocation(distortion, "leftLensCenter"),
                   config.leftLensCenter, SHADER_UNIFORM_VEC2);
    SetShaderValue(distortion, GetShaderLocation(distortion, "rightLensCenter"),
                   config.rightLensCenter, SHADER_UNIFORM_VEC2);
    SetShaderValue(distortion, GetShaderLocation(distortion, "leftScreenCenter"),
                   config.leftScreenCenter, SHADER_UNIFORM_VEC2);
    SetShaderValue(distortion, GetShaderLocation(distortion, "rightScreenCenter"),
                   config.rightScreenCenter, SHADER_UNIFORM_VEC2);

    SetShaderValue(distortion, GetShaderLocation(distortion, "scale"),
                   config.scale, SHADER_UNIFORM_VEC2);
    SetShaderValue(distortion, GetShaderLocation(distortion, "scaleIn"),
                   config.scaleIn, SHADER_UNIFORM_VEC2);
    SetShaderValue(distortion, GetShaderLocation(distortion, "deviceWarpParam"),
                   device.lensDistortionValues, SHADER_UNIFORM_VEC4);
    SetShaderValue(distortion, GetShaderLocation(distortion, "chromaAbParam"),
                   device.chromaAbCorrection, SHADER_UNIFORM_VEC4);

    // Initialize framebuffer for stereo rendering
    // NOTE: Screen size should match HMD aspect ratio
    RenderTexture2D target = LoadRenderTexture(2160, 1200);
                                               // ^^^^^^^^^^^^^^^^^^^^^^^
                                               // Here I changed it from the ScreenWidth/Height to the oculus screen size (differing from the window size)

    // Define the camera to look into our 3d world
    Camera camera = { 0 };
    camera.position = (Vector3){ 5.0f, 2.0f, 5.0f };    // Camera position
    camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };      // Camera looking at point
    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector
    camera.fovy = 60.0f;                                // Camera field-of-view Y
    camera.projection = CAMERA_PERSPECTIVE;             // Camera type

    Vector3 cubePosition = { 0.0f, 0.0f, 0.0f };

    SetCameraMode(camera, CAMERA_FIRST_PERSON);         // Set first person camera mode

    SetTargetFPS(90);                   // Set our game to run at 90 frames-per-second
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())        // Detect window close button or ESC key
    {
        // Update
        //----------------------------------------------------------------------------------
        UpdateCamera(&camera);          // Update camera (simulator mode)
        //----------------------------------------------------------------------------------

        // Draw
        //----------------------------------------------------------------------------------
        BeginTextureMode(target);
            ClearBackground(RAYWHITE);
            BeginVrStereoMode(config);
                BeginMode3D(camera);

                    DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, RED);
                    DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, MAROON);
                    DrawGrid(40, 1.0f);

                EndMode3D();
            EndVrStereoMode();
        EndTextureMode();
        
        BeginDrawing();
            ClearBackground(RAYWHITE);
            BeginShaderMode(distortion);
                DrawTextureRec(target.texture, (Rectangle){ 0, 0, (float)target.texture.width,
                              (float)-target.texture.height }, (Vector2){ 0.0f, 0.0f }, WHITE);
            EndShaderMode();
            DrawFPS(10, 10);
        EndDrawing();
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    UnloadVrStereoConfig(config);   // Unload stereo config

    UnloadRenderTexture(target);    // Unload stereo render fbo
    UnloadShader(distortion);       // Unload distortion shader

    CloseWindow();                  // Close window and OpenGL context
    //--------------------------------------------------------------------------------------

    return 0;
}
@raysan5 raysan5 changed the title [RLGL] BeginTextureMode() doesn't update RLGL's framebuffer size for rendering in VR [rlgl] BeginTextureMode() doesn't update intenal framebuffer size for rendering in VR Apr 2, 2022
@raysan5
Copy link
Owner

raysan5 commented Apr 2, 2022

@FireFlyForLife Oh! That's a good catch! I'll try to review it asap. By the way, nice to know the OpenXR binding! How is it going? Does it require many changes to raylib to work? I imagine you are processing the user-tracking-inputs directly yourself, right?

@FireFlyForLife
Copy link
Contributor Author

FireFlyForLife commented Apr 2, 2022

Hi! very good timing :) I just created a pull request for this. Though you will know best if there is a better solution.

The OpenXR binding is going pretty well, it's a Khronos library so my 7 function header has ballooned into a 1000 lines of source code for implementation. But it's going pretty well, it actually doesn't require any changes to raylib, I only have a function that replaces wraps BeginTextureMode() & EndTextureMode to do some swapchain book keeping but apart from that it should work out of the box thanks to Raylibs VR support! (Apart from this issue of course :p)

For the API I'm trying to keep it small and directly giving you access to the OpenXR system so I don't restrict the user input tracking. (Also because I'm not that experienced yet with the system, I cannot design a good abstraction for it yet :) )

The current API I have is this, I'm planning to open source it when I get it works and when I get the paperwork back from work:

// Setup
bool rlOpenXRSetup();
void rlOpenXRShutdown();

// Update
void rlOpenXRUpdate();
void rlOpenXRUpdateCamera(Camera3D* camera);

// Drawing
bool rlOpenXRBegin();
void rlOpenXREnd();

enum RLOpenXREye { RLOPENXR_LEFT = 1, RLOPENXR_RIGHT = 2, RLOPENXR_BOTH = 3 };
void rlOpenXRBlitFramebuffer(RLOpenXREye eye);

@FireFlyForLife
Copy link
Contributor Author

Actually I had one issue I couldn't work around. I currently cannot go from OpenGL Texture format -> RLGL Texture format. Ideally there would be some way to consistently convert back and forth. Though I get away with it now because in BeginTextureMode() I saw the texture's format field doesn't get touched, so I leave it at -1.

@raysan5
Copy link
Owner

raysan5 commented Apr 24, 2022

@FireFlyForLife Merged the provided fix, I'm still considering if updating RLGL.State.framebufferWidth and RLGL.State.framebufferHeight could be a better solution.

EDIT: Actually DrawMesh() in rmodels uses rlGetFramebufferWidth() and rlGetFramebufferHeight() to draw the models in stereo rendering.

@raysan5
Copy link
Owner

raysan5 commented Apr 24, 2022

It seems current implementation storing viewport data when rlViewport() is called is a problem and breaks rmodels DrawMesh() for VR rendering of models... It requires more work than expected... :(

@raysan5
Copy link
Owner

raysan5 commented Apr 24, 2022

Redesigned, added a bit more code but now it's cleaner.

@raysan5 raysan5 closed this as completed Apr 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants