From 6ac9fabe33cade07e9d8294aa1e0c6e6db73ef03 Mon Sep 17 00:00:00 2001 From: Slamy Date: Thu, 29 Apr 2021 23:44:05 +0200 Subject: [PATCH] Fixed Skybox rendering for OpenVR Rectangle2D supports a second set of normals. Fixed world space corner calculation for head to eye matrices. --- OgreMain/include/OgreCamera.h | 18 ++++- OgreMain/include/OgreRectangle2D2.h | 5 +- OgreMain/include/OgreSceneManager.h | 10 +++ OgreMain/src/OgreCamera.cpp | 72 +++++++++++++++++++ OgreMain/src/OgreFrustum.cpp | 4 +- OgreMain/src/OgreRectangle2D2.cpp | 20 ++++-- OgreMain/src/OgreSceneManager.cpp | 55 +++++++++++--- .../OpenVRCompositorListener.h | 8 +++ .../Tutorial_OpenVR/Tutorial_OpenVR.h | 7 ++ .../Tutorial_OpenVRGameState.cpp | 4 ++ .../Common/GLSL/QuadCameraDirNoUV_vs.glsl | 10 +++ 11 files changed, 193 insertions(+), 20 deletions(-) diff --git a/OgreMain/include/OgreCamera.h b/OgreMain/include/OgreCamera.h index 0657f71e1cb..200d6d1408f 100644 --- a/OgreMain/include/OgreCamera.h +++ b/OgreMain/include/OgreCamera.h @@ -45,17 +45,22 @@ namespace Ogre { struct VrData { Matrix4 mHeadToEye[2]; + Matrix4 mEyeToHead[2]; Matrix4 mProjectionMatrix[2]; - //Matrix4 mLeftToRight; + Matrix4 mProjectionMatrixInverse[2]; Vector3 mLeftToRight; + // The corners are ordered as follows: top-right far, top-left far, bottom-left far, bottom-right far. + Vector3 mWorldSpaceFarCorners[2][4]; + void set( const Matrix4 eyeToHead[2], const Matrix4 projectionMatrix[2] ) { for( int i=0; i<2; ++i ) { - mHeadToEye[i] = eyeToHead[i]; + mEyeToHead[i] = eyeToHead[i]; mProjectionMatrix[i] = projectionMatrix[i]; - mHeadToEye[i] = mHeadToEye[i].inverseAffine(); + mProjectionMatrixInverse[i] = projectionMatrix[i].inverse(); + mHeadToEye[i] = mEyeToHead[i].inverseAffine(); } mLeftToRight = (mHeadToEye[0] * eyeToHead[1]).getTrans(); } @@ -244,6 +249,8 @@ namespace Ogre { static void setDefaultSortMode( CameraSortMode sortMode ) { msDefaultSortMode = sortMode; } static CameraSortMode getDefaultSortMode( void ) { return msDefaultSortMode; } + /// Recalculate Vr world space corners for Sky rendering + void updateVrWorldSpaceFarCorners(); protected: // Internal functions for calcs bool isViewOutOfDate(void) const; @@ -700,6 +707,11 @@ namespace Ogre { bool isVisible(const Vector3& vert, FrustumPlane* culledBy = 0) const; /// @copydoc Frustum::getWorldSpaceCorners const Vector3* getWorldSpaceCorners(void) const; + /** Gets the 4 far world space corners of the frustum. + @remarks + The corners are ordered as follows: top-right far, top-left far, bottom-left far, bottom-right far. + */ + const Vector3* getVrWorldSpaceFarCorners(size_t eyeIdx) const; /// @copydoc Frustum::getFrustumPlane const Plane& getFrustumPlane( unsigned short plane ) const; /// @copydoc Frustum::projectSphere diff --git a/OgreMain/include/OgreRectangle2D2.h b/OgreMain/include/OgreRectangle2D2.h index 6a87b7b0fd6..b93c882aa64 100644 --- a/OgreMain/include/OgreRectangle2D2.h +++ b/OgreMain/include/OgreRectangle2D2.h @@ -68,8 +68,7 @@ namespace Ogre bool mChanged; uint32 mGeometryFlags; - Vector3 mNormals[NumCorners]; - + Vector3 mNormals[NumCorners*2]; Vector2 mPosition; Vector2 mSize; @@ -93,6 +92,8 @@ namespace Ogre void setNormals( const Vector3 &upperLeft, const Vector3 &bottomLeft, // const Vector3 &upperRight, const Vector3 &bottomRight ); + void setStereoNormals( const Vector3 &upperLeft, const Vector3 &bottomLeft, // + const Vector3 &upperRight, const Vector3 &bottomRight ); void setHollowRectRadius( Real radius ); Real getHollowRectRadius( void ) const { return mNormals[0].x; } diff --git a/OgreMain/include/OgreSceneManager.h b/OgreMain/include/OgreSceneManager.h index 35357b9f0c3..3a9a25d1661 100644 --- a/OgreMain/include/OgreSceneManager.h +++ b/OgreMain/include/OgreSceneManager.h @@ -522,6 +522,8 @@ namespace Ogre { /// For VR optimization RadialDensityMask *mRadialDensityMask; + bool mSkyStereo{false}; + // Fog FogMode mFogMode; ColourValue mFogColour; @@ -1178,6 +1180,14 @@ namespace Ogre { void setRadialDensityMask( bool bEnabled, const float radius[3] ); RadialDensityMask* getRadialDensityMask(void) const { return mRadialDensityMask; } + /** + * Must be used before setSky. Activates instanced stereo mode for OpenVR applications + */ + void setSkyStereoMode (bool bEnabled) + { + mSkyStereo = bEnabled; + } + /** Gets the SceneNode at the root of the scene hierarchy. @remarks The entire scene is held as a hierarchy of nodes, which diff --git a/OgreMain/src/OgreCamera.cpp b/OgreMain/src/OgreCamera.cpp index fa6d97f12de..a2265cba205 100644 --- a/OgreMain/src/OgreCamera.cpp +++ b/OgreMain/src/OgreCamera.cpp @@ -836,6 +836,73 @@ namespace Ogre { return getProjectionMatrixWithRSDepth(); } //----------------------------------------------------------------------- + void Camera::updateVrWorldSpaceFarCorners() + { + if( !mVrData ) + { + return; + } + + updateView(); + Matrix4 eyeToWorld = mViewMatrix.inverseAffine(); + + // Note: Even though we can dealing with general projection matrix here, + // but because it's incompatibly with infinite far plane, thus, we + // still need to working with projection parameters. + + // Calc near plane corners + + for( size_t eyeIdx=0u; eyeIdx<2u; ++eyeIdx ) + { + Real nearLeft, nearRight, nearBottom, nearTop; + + Matrix4 invProj = mVrData->mProjectionMatrixInverse[eyeIdx]; + + Vector4 topLeft( -1.0f, 1.0f, -1.0f, 1.0f ); + Vector4 topRight( 1.0f, 1.0f, -1.0f, 1.0f ); + Vector4 bottomRight( 1.0f, -1.0f, -1.0f, 1.0f ); + Vector4 bottomLeft( -1.0f, -1.0f, -1.0f, 1.0f ); + + topLeft = invProj * topLeft; + topRight = invProj * topRight; + bottomLeft = invProj * bottomLeft; + bottomRight = invProj * bottomRight; + + // Rotate the view according to the HeadToEye matrix from OpenVR. + // Important for asymmetrical frustums which are present for some + // Oculus headsets and Windows Mixed Reality + Matrix4 eyeToHead= mVrData->mEyeToHead[eyeIdx]; + eyeToHead.setTrans(Vector3::ZERO); + + topLeft = eyeToHead * topLeft; + topRight = eyeToHead * topRight; + bottomLeft = eyeToHead * bottomLeft; + bottomRight = eyeToHead * bottomRight; + + Vector4 nearTopLeft = topLeft * mNearDist; + Vector4 nearTopRight = topRight * mNearDist; + Vector4 nearBottomLeft = bottomLeft * mNearDist; + Vector4 nearBottomRight = bottomRight * mNearDist; + + // Treat infinite fardist as some arbitrary far value + Real farDist = (mFarDist == 0) ? 100000 : mFarDist; + + // Calc far plane corners + Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; + Vector4 farTopLeft = nearTopLeft * radio; + Vector4 farTopRight = nearTopRight * radio; + Vector4 farBottomLeft = nearBottomLeft * radio; + Vector4 farBottomRight = nearBottomRight * radio; + + // far + mVrData->mWorldSpaceFarCorners[eyeIdx][0] = eyeToWorld.transformAffine(Vector3(farTopRight.x, farTopRight.y, -farDist)); + mVrData->mWorldSpaceFarCorners[eyeIdx][1] = eyeToWorld.transformAffine(Vector3(farTopLeft.x, farTopLeft.y, -farDist)); + mVrData->mWorldSpaceFarCorners[eyeIdx][2] = eyeToWorld.transformAffine(Vector3(farBottomLeft.x, farBottomLeft.y, -farDist)); + mVrData->mWorldSpaceFarCorners[eyeIdx][3] = eyeToWorld.transformAffine(Vector3(farBottomRight.x, farBottomRight.y, -farDist)); + } + + } + //----------------------------------------------------------------------- bool Camera::getAutoAspectRatio(void) const { return mAutoAspectRatio; @@ -894,6 +961,11 @@ namespace Ogre { } } //----------------------------------------------------------------------- + const Vector3* Camera::getVrWorldSpaceFarCorners(size_t eyeIdx) const + { + return mVrData ? mVrData->mWorldSpaceFarCorners[eyeIdx] : nullptr; + } + //----------------------------------------------------------------------- const Plane& Camera::getFrustumPlane( unsigned short plane ) const { if (mCullFrustum) diff --git a/OgreMain/src/OgreFrustum.cpp b/OgreMain/src/OgreFrustum.cpp index 038e44b0f7d..b6667c8775a 100644 --- a/OgreMain/src/OgreFrustum.cpp +++ b/OgreMain/src/OgreFrustum.cpp @@ -888,7 +888,7 @@ namespace Ogre { // Treat infinite fardist as some arbitrary far value Real farDist = (mFarDist == 0) ? 100000 : mFarDist; - // Calc far palne corners + // Calc far plane corners Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; Real farLeft = nearLeft * radio; Real farRight = nearRight * radio; @@ -940,7 +940,7 @@ namespace Ogre { // Treat infinite fardist as some arbitrary far value Real farDist = (customFarPlane == 0) ? 100000 : customFarPlane; - // Calc far palne corners + // Calc far plane corners Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; Real farLeft = nearLeft * radio; Real farRight = nearRight * radio; diff --git a/OgreMain/src/OgreRectangle2D2.cpp b/OgreMain/src/OgreRectangle2D2.cpp index 099c1a9fd18..d15032b063d 100644 --- a/OgreMain/src/OgreRectangle2D2.cpp +++ b/OgreMain/src/OgreRectangle2D2.cpp @@ -239,7 +239,7 @@ namespace Ogre if( bHasNormals ) { for( size_t j = 0u; j < 3u; ++j ) - *vertexData++ = mNormals[0][j]; + *vertexData++ = mNormals[0 + 4 * i][j]; } // Top left @@ -250,7 +250,7 @@ namespace Ogre if( bHasNormals ) { for( size_t j = 0u; j < 3u; ++j ) - *vertexData++ = mNormals[1][j]; + *vertexData++ = mNormals[1 + 4 * i][j]; } // Bottom right @@ -261,7 +261,7 @@ namespace Ogre if( bHasNormals ) { for( size_t j = 0u; j < 3u; ++j ) - *vertexData++ = mNormals[2][j]; + *vertexData++ = mNormals[2 + 4 * i][j]; } if( isQuad() ) @@ -274,7 +274,7 @@ namespace Ogre if( bHasNormals ) { for( size_t j = 0u; j < 3u; ++j ) - *vertexData++ = mNormals[3][j]; + *vertexData++ = mNormals[3 + 4 * i][j]; } } } @@ -302,6 +302,18 @@ namespace Ogre mNormals[CornerUpperRight] = upperRight; mChanged = true; } + + void Rectangle2D::setStereoNormals( const Vector3 &upperLeft, const Vector3 &bottomLeft, + const Vector3 &upperRight, const Vector3 &bottomRight ) + { + OGRE_ASSERT_MEDIUM( getBufferType() != BT_IMMUTABLE || mVaoPerLod[0].empty() ); + OGRE_ASSERT_MEDIUM( hasNormals() || mVaoPerLod[0].empty() ); + mNormals[4 + CornerBottomLeft] = bottomLeft; + mNormals[4 + CornerUpperLeft] = upperLeft; + mNormals[4 + CornerBottomRight] = bottomRight; + mNormals[4 + CornerUpperRight] = upperRight; + mChanged = true; + } //----------------------------------------------------------------------------------- void Rectangle2D::setHollowRectRadius( Real radius ) { diff --git a/OgreMain/src/OgreSceneManager.cpp b/OgreMain/src/OgreSceneManager.cpp index 0a5c9812b2c..9780ff3983b 100644 --- a/OgreMain/src/OgreSceneManager.cpp +++ b/OgreMain/src/OgreSceneManager.cpp @@ -1005,8 +1005,13 @@ void SceneManager::setSky( bool bEnabled, SkyMethod skyMethod, TextureGpu *textu &mEntityMemoryManager[SCENE_STATIC], this ); // We can't use BT_DYNAMIC_* because the scene may be rendered from multiple cameras // in the same frame, and dynamic supports only one set of values per frame - mSky->initialize( BT_DEFAULT, - Rectangle2D::GeometryFlagQuad | Rectangle2D::GeometryFlagNormals ); + + uint32 skyGeometryFlags=Rectangle2D::GeometryFlagQuad | Rectangle2D::GeometryFlagNormals; + + if (mSkyStereo) + skyGeometryFlags|=Rectangle2D::GeometryFlagStereo; + + mSky->initialize( BT_DEFAULT,skyGeometryFlags); mSky->setGeometry( -Ogre::Vector2::UNIT_SCALE, Ogre::Vector2( 2.0f ) ); mSky->setRenderQueueGroup( 212u ); // Render after most stuff mSceneRoot[SCENE_STATIC]->attachObject( mSky ); @@ -1064,6 +1069,13 @@ void SceneManager::setSky( bool bEnabled, SkyMethod skyMethod, TextureGpu *textu tu->setTexture( texture ); mSky->setMaterial( mSkyMaterial ); + + GpuProgramParametersSharedPtr vsParams = pass->getVertexProgramParameters(); + vsParams->setNamedConstant( "ogreBaseVertex", (float)mSky->getVaos( VpNormal ) + .back() + ->getBaseVertexBuffer() + ->_getFinalBufferStart() ); + } else { @@ -1381,18 +1393,43 @@ void SceneManager::_renderPhase02(Camera* camera, const Camera *lodCamera, if( mSky && mIlluminationStage != IRS_RENDER_TO_TEXTURE ) { - const Vector3 *corners = camera->getWorldSpaceCorners(); + const Vector3 *corners; const Vector3 &cameraPos = camera->getDerivedPosition(); - const Real invFarPlane = 1.0f / camera->getFarClipDistance(); Vector3 cameraDirs[4]; - cameraDirs[0] = ( corners[5] - cameraPos ) * invFarPlane; - cameraDirs[1] = ( corners[6] - cameraPos ) * invFarPlane; - cameraDirs[2] = ( corners[4] - cameraPos ) * invFarPlane; - cameraDirs[3] = ( corners[7] - cameraPos ) * invFarPlane; - mSky->setNormals( cameraDirs[0], cameraDirs[1], cameraDirs[2], cameraDirs[3] ); + if (!mSky->isStereo()) + { + corners = camera->getWorldSpaceCorners(); + cameraDirs[0] = ( corners[5] - cameraPos ) * invFarPlane; + cameraDirs[1] = ( corners[6] - cameraPos ) * invFarPlane; + cameraDirs[2] = ( corners[4] - cameraPos ) * invFarPlane; + cameraDirs[3] = ( corners[7] - cameraPos ) * invFarPlane; + + mSky->setNormals( cameraDirs[0], cameraDirs[1], cameraDirs[2], cameraDirs[3] ); + mSky->setStereoNormals( cameraDirs[0], cameraDirs[1], cameraDirs[2], cameraDirs[3] ); + } + else + { + camera->updateVrWorldSpaceFarCorners(); + + corners = camera->getVrWorldSpaceFarCorners(0); + cameraDirs[0] = ( corners[1] - cameraPos ) * invFarPlane; + cameraDirs[1] = ( corners[2] - cameraPos ) * invFarPlane; + cameraDirs[2] = ( corners[0] - cameraPos ) * invFarPlane; + cameraDirs[3] = ( corners[3] - cameraPos ) * invFarPlane; + mSky->setNormals( cameraDirs[0], cameraDirs[1], cameraDirs[2], cameraDirs[3] ); + + corners = camera->getVrWorldSpaceFarCorners(1); + cameraDirs[0] = ( corners[1] - cameraPos ) * invFarPlane; + cameraDirs[1] = ( corners[2] - cameraPos ) * invFarPlane; + cameraDirs[2] = ( corners[0] - cameraPos ) * invFarPlane; + cameraDirs[3] = ( corners[3] - cameraPos ) * invFarPlane; + mSky->setStereoNormals( cameraDirs[0], cameraDirs[1], cameraDirs[2], cameraDirs[3] ); + } + mSky->update(); + } if( mRadialDensityMask && mIlluminationStage != IRS_RENDER_TO_TEXTURE && diff --git a/Samples/2.0/Tutorials/Tutorial_OpenVR/OpenVRCompositorListener.h b/Samples/2.0/Tutorials/Tutorial_OpenVR/OpenVRCompositorListener.h index c20d1f5b633..1be954f2a5e 100644 --- a/Samples/2.0/Tutorials/Tutorial_OpenVR/OpenVRCompositorListener.h +++ b/Samples/2.0/Tutorials/Tutorial_OpenVR/OpenVRCompositorListener.h @@ -15,7 +15,15 @@ #define nullptr (0) #endif #endif + +#ifdef __MINGW32__ +// For compilation on Windows with MinGW a special header must be used +#include "openvr_mingw.hpp" +#else #include "openvr.h" +#endif + + #if __cplusplus <= 199711L #ifdef OgreDemoNullptrDefined #undef OgreDemoNullptrDefined diff --git a/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVR.h b/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVR.h index 6c9a81c3667..ada82f49951 100644 --- a/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVR.h +++ b/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVR.h @@ -10,7 +10,14 @@ #define nullptr (0) #endif #endif + +#ifdef __MINGW32__ +// For compilation on Windows with MinGW a special header must be used +#include "openvr_mingw.hpp" +#else #include "openvr.h" +#endif + #if __cplusplus <= 199711L #ifdef OgreDemoNullptrDefined #undef OgreDemoNullptrDefined diff --git a/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVRGameState.cpp b/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVRGameState.cpp index 444f6b8e31a..46209c9d690 100644 --- a/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVRGameState.cpp +++ b/Samples/2.0/Tutorials/Tutorial_OpenVR/Tutorial_OpenVRGameState.cpp @@ -253,6 +253,10 @@ namespace Demo mCameraController = new CameraController( mGraphicsSystem, false ); TutorialGameState::createScene01(); + + sceneManager->setSkyStereoMode(true); + sceneManager->setSky(true, Ogre::SceneManager::SkyCubemap, "SaintPetersBasilica.dds", + Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); } //----------------------------------------------------------------------------------- void Tutorial_OpenVRGameState::update( float timeSinceLast ) diff --git a/Samples/Media/2.0/scripts/materials/Common/GLSL/QuadCameraDirNoUV_vs.glsl b/Samples/Media/2.0/scripts/materials/Common/GLSL/QuadCameraDirNoUV_vs.glsl index 764a77f6ef6..63c3f675c8a 100644 --- a/Samples/Media/2.0/scripts/materials/Common/GLSL/QuadCameraDirNoUV_vs.glsl +++ b/Samples/Media/2.0/scripts/materials/Common/GLSL/QuadCameraDirNoUV_vs.glsl @@ -1,9 +1,13 @@ #version ogre_glsl_ver_330 +#extension GL_ARB_shader_viewport_layer_array : require vulkan_layout( OGRE_POSITION ) in vec2 vertex; vulkan_layout( OGRE_NORMAL ) in vec3 normal; +in int gl_InstanceID; + vulkan( layout( ogre_P0 ) uniform Params { ) + uniform float ogreBaseVertex; uniform vec2 rsDepthRange; uniform mat4 worldViewProj; vulkan( }; ) @@ -22,7 +26,13 @@ out block void main() { gl_Position.xy = (worldViewProj * vec4( vertex.xy, 0, 1.0f )).xy; + + // TODO Move the second instance aside. There must be a way to make this more clean. But how? + gl_Position.x += 3.5f*gl_InstanceID; + gl_Position.z = rsDepthRange.y; gl_Position.w = 1.0f; outVs.cameraDir.xyz = normal.xyz; + + gl_ViewportIndex = (gl_VertexID - ogreBaseVertex) >= 2 ? 1 : 0; }