Skip to main content
Up and Coming
June 23, 2013

OpenSceneGraph

I've started working on an integration with OpenSceneGraph (http://www.openscenegraph.org/) and although I don't have a Rift to test with yet what I've done looks pretty close to the Oculus World demo so I'm hoping I've done it right. If there are any OSG developers out there who could test out my code I'd really appreciate it. I found GLSL versions of the shaders in the OpenHMD project (http://openhmd.net/) which are referenced as vertexShaderSource and fragmentShaderSource. I based my work on the night vision effect example from the OpenSceneGraph Cookbook (http://www.packtpub.com/openscenegrap-3-for-advanced-3d-programming-using-api-cookbook/book) and my code uses the functions from that example to create the RTT & HUD cameras as well as the quads. The source is available at https://github.com/xarray/osgRecipes. I have pseudocode where the scene is added to the cameras (step five in the cookbook) and where the cameras are added to the root node (step 7 in the cookbook) because I'm also integrating with a derived type of renderer but you should be able to replace them with what's in the example.




// Because I don't have a Rift yet I just made a OVR::HMDInfo and OVR::Util::Render::StereoConfig and guessed at some values.
myHMDInfo.HResolution = 1280;
myHMDInfo.VResolution = 800;
myHMDInfo.HScreenSize = 0.14976;
myHMDInfo.VScreenSize = 0.0935;
myHMDInfo.VScreenCenter = myHMDInfo.VScreenSize / 2;
myHMDInfo.EyeToScreenDistance = 0.05; //????
myHMDInfo.LensSeparationDistance = 0.03; //????
myHMDInfo.InterpupillaryDistance = 0.025; //?????????

myStereoConfig.SetHMDInfo(myHMDInfo);


const int textureWidth = myHMDInfo.HResolution / 2;
const int textureHeight = myHMDInfo.VResolution;

// Setup textures for the RTT cameras
osg::ref_ptr<osg::Texture2D> leftEyeTex2D = new osg::Texture2D;
leftEyeTex2D->setTextureSize( textureWidth, textureHeight );
leftEyeTex2D->setInternalFormat( GL_RGBA );
osg::ref_ptr<osg::Texture2D> rightEyeTex2D = new osg::Texture2D;
rightEyeTex2D->setTextureSize( textureWidth, textureHeight );
rightEyeTex2D->setInternalFormat( GL_RGBA );

// Initialize RTT cameras for each eye
osg::ref_ptr<osg::Camera> leftEyeRTTCamera = osgCookBook::createRTTCamera(
osg::Camera::COLOR_BUFFER, leftEyeTex2D.get());
OVR::Util::Render::StereoEyeParams leftEyeParams =
myStereoConfig.GetEyeRenderParams(OVR::Util::Render::StereoEye_Left);
osg::Matrixf leftEyeTranslation = osg::Matrixf::translate(
leftEyeParams.ViewAdjust.M[0][3], leftEyeParams.ViewAdjust.M[2][3],
leftEyeParams.ViewAdjust.M[2][3]);
leftEyeRTTCamera->setViewMatrix(leftEyeTranslation);
leftEyeRTTCamera->setProjectionMatrixAsPerspective(
myStereoConfig.GetYFOVDegrees(), myStereoConfig.GetAspect(), 0.3, 5000);
osg::ref_ptr<osg::Camera> rightEyeRTTCamera = osgCookBook::createRTTCamera(
osg::Camera::COLOR_BUFFER, rightEyeTex2D.get());
OVR::Util::Render::StereoEyeParams rightEyeParams =
myStereoConfig.GetEyeRenderParams(OVR::Util::Render::StereoEye_Right);
osg::Matrixf rightEyeTranslation = osg::Matrixf::translate(
rightEyeParams.ViewAdjust.M[0][3], rightEyeParams.ViewAdjust.M[2][3],
rightEyeParams.ViewAdjust.M[2][3]);
rightEyeRTTCamera->setViewMatrix(rightEyeTranslation);
rightEyeRTTCamera->setProjectionMatrixAsPerspective(
myStereoConfig.GetYFOVDegrees(), myStereoConfig.GetAspect(), 0.3, 5000);

// add your scene to the camera, should be something like:
// leftEyeRTTCamera->addChild( scene.get() );
// rightEyeRTTCamera->addChild( scene.get() );

// Create HUD cameras for each eye
osg::ref_ptr<osg::Camera> leftEyeHUDCamera =
osgCookBook::createHUDCamera(0.0, 1.0, 0.0, 1.0);
osg::ref_ptr<osg::Camera> rightEyeHUDCamera =
osgCookBook::createHUDCamera(0.0, 1.0, 0.0, 1.0);

// Create quads on each camera
leftEyeHUDCamera->addChild( osgCookBook::createScreenQuad(
1.0f, 1.0f) );
leftEyeHUDCamera->setViewport(
new osg::Viewport(0, 0, myHMDInfo.HResolution / 2, myHMDInfo.VResolution));
rightEyeHUDCamera->addChild( osgCookBook::createScreenQuad(
1.0f, 1.0f) );
rightEyeHUDCamera->setViewport(
new osg::Viewport(myHMDInfo.HResolution / 2, 0, myHMDInfo.HResolution / 2, myHMDInfo.VResolution));

// Set up shaders (from the OpenHMD project)
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader( new osg::Shader(osg::Shader::VERTEX,
vertexShaderSource) );
program->addShader( new osg::Shader(osg::Shader::FRAGMENT,
fragmentShaderSource) );

// Configure state sets for both eyes
osg::StateSet* leftEyeStateSet = leftEyeHUDCamera->getOrCreateStateSet();
leftEyeStateSet->setTextureAttributeAndModes( 0, leftEyeTex2D.get() );
leftEyeStateSet->setAttributeAndModes( program.get() );
leftEyeStateSet->addUniform( new osg::Uniform("warpTexture", 0) );

osg::StateSet* rightEyeStateSet = rightEyeHUDCamera->getOrCreateStateSet();
rightEyeStateSet->setTextureAttributeAndModes( 0, rightEyeTex2D.get() );
rightEyeStateSet->setAttributeAndModes( program.get() );
rightEyeStateSet->addUniform( new osg::Uniform("warpTexture", 0) );


// Add everything to the root, should be something like:
//osg::ref_ptr<osg::Group> root = ?;
//root->addChild( leftEyeRTTCamera.get().get() );
//root->addChild( leftEyeHUDCamera.get().get() );
//root->addChild( rightEyeRTTCamera.get().get() );
//root->addChild( rightEyeHUDCamera.get().get() );
//root->addChild( hudCamera.get() );
//root->addChild( scene.get() );

    54 replies

    Up and Coming
    September 23, 2013
    "Afuerg" wrote:
    Great project! But I'm asking myself why you implemented all the calculations yourself, when the Oculus SDK offers the StereoConfig class which is supposed to handle this kind of calculations.
    Is there a specific reason to not use it?


    I would like to say it had some really good reason, such as clarity or extendibility. But the actual truth is that I started reading the API docs and wrote most of the code as I worked my way though it. When I reached the chapter about the StereoConfig util class I already had most of the functionality up and running. And then it just felt silly to change strategy.

    But for me personally it gave good insight into how the internals of the StereoConfig class was working.
    Up and Coming
    October 4, 2013
    I have moved the Oculus enabling code from a class inheriting from osg::Group to class which inherits osgViewer::ViewConfig. If you have OpenSceneGraph 3.2.0 or later installed you can use this implementation as a simple drop in solution. So only the following code is necessary to make you application Rift Enabled:

    // Create Oculus View Config
    osg::ref_ptr<OculusViewConfig> oculusViewConfig = new OculusViewConfig;
    // Create viewer
    osgViewer::Viewer viewer(arguments);
    // Apply view config
    viewer.apply(oculusViewConfig);
    // Add loaded model to viewer
    viewer.setSceneData(loadedModel);
    // Start Viewer
    viewer.run();
    Up and Coming
    August 1, 2014
    Hi all,

    I have just pushed the first steps of Oculus SDK 0.4 support to OsgOculusViewer.

    The integration uses Client distortion rendering mode, because I ended up with a catch 22 situation with the SDK assisted rendering.

    The SDK 0.4 integration should be considered as work in progress as many features are still missing:
    • Positional tracking

    • Direct rendering

    • TimeWarp


    Nor has it been tested on a DK2 unit, since I have not received mine yet.

    You can find the code here:
    http://github.com/bjornblissing/osgoculusviewer/tree/sdk-v0.4

    Please feel free to test it out and report/fix any bugs or missing features.

    Best regards

    Björn
    Up and Coming
    August 14, 2014
    I have just pushed the latest changes to GitHub. This includes support for:


    • Version 0.4.1 of the Oculus SDK.

    • Positional Tracking (only with DK2 camera)

    • Time warp

    • Three separate examples of how to use the library:
      - Standard Viewer
      - Standard Viewer with ViewConfig (only OpenSceneGraph 3.2 and later)
      - Composite Viewer

    • Mac support


    Still only work with extended desktop mode, i.e. no direct rendering support yet.
    Up and Coming
    August 19, 2014
    The 0.4 Development branch has been merged into master. So now you will find the latest source at:
    http://github.com/bjornblissing/osgoculusviewer
    Up and Coming
    September 28, 2014
    "pixelminer" wrote:

    The integration uses Client distortion rendering mode, because I ended up with a catch 22 situation with the SDK assisted rendering.


    I have SDK based rendering working well in my OSG application so I thought I'd share some of the techniques I used.

    Because OSG uses lazy initialization I had to force my render to texture targets to be fully initialized as soon as they're created otherwise I'd get a crash when calling ovrHmd_EndFrame. I did this by calling osg::Texture2D::allocateMipmapLevels and then immediately calling osg::Texture2D::apply on my textures.

    I ran into some flickering issues because both OSG and OVR were calling swapbuffers. OSG makes it easy enough to disable that; just call osg::GraphicsContext::setSwapCallback and pass in a osg::GraphicsContext::SwapCallback class with the swapBuffersImplementation defined as a no-op.
    Up and Coming
    October 1, 2014
    I would like to include SDK-assisted rendering in my library of OpenSceneGraph examples.
    I have a lot of small questions regarding your implementation. But I guess I start with the broad questions first:
    1. What type of Viewer configuration are you using? Master-Slave or CompositeViewer?
    2. Do you use a standard frame loop (just calling viewer.run() ) or have you a custom frame loop?
    3. How do you pass your osg::Texture2D objects to the ovrHmd_EndFrame() function i.e. conversion mechanism going from osg::Texture2D to ovrTexture?
    Up and Coming
    October 1, 2014
    "pixelminer" wrote:
    1. What type of Viewer configuration are you using? Master-Slave or CompositeViewer?


    I have a osgViewer::View to which I attach my RTT Cameras as slaves.

    "pixelminer" wrote:
    2. Do you use a standard frame loop (just calling viewer.run() ) or have you a custom frame loop?


    I have a customized frame loop which performs a lot of operations such as customized culling.

    "pixelminer" wrote:
    3. How do you pass your osg::Texture2D objects to the ovrHmd_EndFrame() function i.e. conversion mechanism going from osg::Texture2D to ovrTexture?


    The texture itself isn't converted, rather you're filling out the ovrGLTextureData structure which includes (among other things) the texture ID. I started with the implementation in Texture::Get_ovrTexture() from the Oculus World Demo's Render_GL_Device.cpp. The tricky bit was getting OSG to give up the texture ID; I had to call osg::Texture::getTextureObject to get the object so I could call osg::Texture::TextureObject::id on it and pass that into the TexId field of the struct.
    Up and Coming
    October 1, 2014
    Hi Jeff,

    I have made some progress. But when I try to run apply() on my render texture as you describe above it get the following error:
    Error: In Texture::Extensions::setupGLExtensions(..) OpenGL version test failed, requires valid graphics context.


    And then I hit a break point in libOVR when I call:
    ovrHmd_ConfigureRendering()


    Even though I realize my viewer before creating the render texture and invoke ovrHmd_ConfigureRendering I apparently lack a valid GL context.

    Any suggestions?
    Up and Coming
    October 1, 2014
    Sorry I completely forgot to address that bit; I'd run into the same problem. I made a osg::Drawable whose drawImplementation calls my initialization function that calls ovrHmd_ConfigureRendering. I attach an instance of the drawable to an osg::Geode which I put into a osg::Group and add to the root of my scene graph. This way the rendering isn't configured until after everything is fully initialized which fixed my problem. There are a lot of posts in the forums speculating on the proper order of initialization; I really wish the OVR docs were more authoritative on this topic.