diff options
Diffstat (limited to 'LibOVR/Src/Util/Util_Render_Stereo.h')
-rw-r--r-- | LibOVR/Src/Util/Util_Render_Stereo.h | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/LibOVR/Src/Util/Util_Render_Stereo.h b/LibOVR/Src/Util/Util_Render_Stereo.h new file mode 100644 index 0000000..326059e --- /dev/null +++ b/LibOVR/Src/Util/Util_Render_Stereo.h @@ -0,0 +1,498 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : Util_Render_Stereo.h +Content : Sample stereo rendering configuration classes. +Created : October 22, 2012 +Authors : Michael Antonov, Tom Forsyth + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Util_Render_Stereo_h +#define OVR_Util_Render_Stereo_h + +#include "../OVR_Stereo.h" + + +namespace OVR { + +class SensorFusion; + +namespace Util { namespace Render { + + + +//----------------------------------------------------------------------------------- +// **** Useful debug functions. +// +// Purely for debugging - the results are not very end-user-friendly. +char const* GetDebugNameEyeCupType ( EyeCupType eyeCupType ); +char const* GetDebugNameHmdType ( HmdTypeEnum hmdType ); + + + +//----------------------------------------------------------------------------------- +// **** Higher-level utility functions. + +Sizei CalculateRecommendedTextureSize ( HmdRenderInfo const &hmd, + bool bRendertargetSharedByBothEyes, + float pixelDensityInCenter = 1.0f ); + +FovPort CalculateRecommendedFov ( HmdRenderInfo const &hmd, + StereoEye eyeType, + bool bMakeFovSymmetrical = false); + +StereoEyeParams CalculateStereoEyeParams ( HmdRenderInfo const &hmd, + StereoEye eyeType, + Sizei const &actualRendertargetSurfaceSize, + bool bRendertargetSharedByBothEyes, + bool bRightHanded = true, + float zNear = 0.01f, float zFar = 10000.0f, + Sizei const *pOverrideRenderedPixelSize = NULL, + FovPort const *pOverrideFovport = NULL, + float zoomFactor = 1.0f ); + +Vector3f CalculateEyeVirtualCameraOffset(HmdRenderInfo const &hmd, + StereoEye eyeType, bool bMonoRenderingMode ); + + +// These are two components from StereoEyeParams that can be changed +// very easily without full recomputation of everything. +struct ViewportScaleAndOffset +{ + Recti RenderedViewport; + ScaleAndOffset2D EyeToSourceUV; +}; + +// Three ways to override the size of the render view dynamically. +// None of these require changing the distortion parameters or the regenerating the distortion mesh, +// and can be called every frame if desired. +ViewportScaleAndOffset ModifyRenderViewport ( StereoEyeParams const ¶ms, + Sizei const &actualRendertargetSurfaceSize, + Recti const &renderViewport ); + +ViewportScaleAndOffset ModifyRenderSize ( StereoEyeParams const ¶ms, + Sizei const &actualRendertargetSurfaceSize, + Sizei const &requestedRenderSize, + bool bRendertargetSharedByBothEyes = false ); + +ViewportScaleAndOffset ModifyRenderDensity ( StereoEyeParams const ¶ms, + Sizei const &actualRendertargetSurfaceSize, + float pixelDensity = 1.0f, + bool bRendertargetSharedByBothEyes = false ); + + +//----------------------------------------------------------------------------------- +// ***** StereoConfig + +// StereoConfig maintains a scene stereo state and allow switching between different +// stereo rendering modes. To support rendering, StereoConfig keeps track of HMD +// variables such as screen size, eye-to-screen distance and distortion, and computes +// extra data such as FOV and distortion center offsets based on it. Rendering +// parameters are returned though StereoEyeParams for each eye. +// +// Beyond regular 3D projection, this class supports rendering a 2D orthographic +// surface for UI and text. The 2D surface will be defined by CreateOrthoSubProjection(). +// The (0,0) coordinate corresponds to eye center location. +// +// Applications are not required to use this class, but they should be doing very +// similar sequences of operations, and it may be useful to start with this class +// and modify it. + +struct StereoEyeParamsWithOrtho +{ + StereoEyeParams StereoEye; + Matrix4f OrthoProjection; +}; + +struct ViewportScaleAndOffsetBothEyes +{ + ViewportScaleAndOffset Left; + ViewportScaleAndOffset Right; +}; + +class StereoConfig +{ +public: + + // StereoMode describes rendering modes that can be used by StereoConfig. + // These modes control whether stereo rendering is used or not (Stereo_None), + // and how it is implemented. + enum StereoMode + { + Stereo_None = 0, // Single eye + Stereo_LeftRight_Multipass = 1, // One frustum per eye + }; + + + StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass); + + //--------------------------------------------------------------------------------------------- + // *** Core functions - every app MUST call these functions at least once. + + // Sets HMD parameters; also initializes distortion coefficients. + void SetHmdRenderInfo(const HmdRenderInfo& hmd); + + // Set the physical size of the rendertarget surface the app created, + // and whether one RT is shared by both eyes, or each eye has its own RT: + // true: both eyes are rendered to the same RT. Left eye starts at top-left, right eye starts at top-middle. + // false: each eye is rendered to its own RT. Some GPU architectures prefer this arrangement. + // Typically, the app would call CalculateRecommendedTextureSize() to suggest the choice of RT size. + // This setting must be exactly the size of the actual RT created, or the UVs produced will be incorrect. + // If the app wants to render to a subsection of the RT, it should use SetRenderSize() + void SetRendertargetSize (Size<int> const rendertargetSize, + bool rendertargetIsSharedByBothEyes ); + + // Returns full set of Stereo rendering parameters for the specified eye. + const StereoEyeParamsWithOrtho& GetEyeRenderParams(StereoEye eye); + + + + //--------------------------------------------------------------------------------------------- + // *** Optional functions - an app may call these to override default behaviours. + + const HmdRenderInfo& GetHmdRenderInfo() const { return Hmd; } + + // Returns the recommended size of rendertargets. + // If rendertargetIsSharedByBothEyes is true, this is the size of the combined buffer. + // If rendertargetIsSharedByBothEyes is false, this is the size of each individual buffer. + // pixelDensityInCenter may be set to any number - by default it will match the HMD resolution in the center of the image. + // After creating the rendertargets, the application MUST call SetRendertargetSize() with the actual size created + // (which can be larger or smaller as the app wishes, but StereoConfig needs to know either way) + Sizei CalculateRecommendedTextureSize ( bool rendertargetSharedByBothEyes, + float pixelDensityInCenter = 1.0f ); + + // Sets a stereo rendering mode and updates internal cached + // state (matrices, per-eye view) based on it. + void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; } + StereoMode GetStereoMode() const { return Mode; } + + // Sets the fieldOfView that the 2D coordinate area stretches to. + void Set2DAreaFov(float fovRadians); + + // Really only for science experiments - no normal app should ever need to override + // the HMD's lens descriptors. Passing NULL removes the override. + // Supply both = set left and right. + // Supply just left = set both to the same. + // Supply neither = remove override. + void SetLensOverride ( LensConfig const *pLensOverrideLeft = NULL, + LensConfig const *pLensOverrideRight = NULL ); + + // Override the rendered FOV in various ways. All angles in tangent units. + // This is not clamped to the physical FOV of the display - you'll need to do that yourself! + // Supply both = set left and right. + // Supply just left = set both to the same. + // Supply neither = remove override. + void SetFov ( FovPort const *pfovLeft = NULL, + FovPort const *pfovRight = NULL ); + + void SetFovPortRadians ( float horizontal, float vertical ) + { + FovPort fov = FovPort::CreateFromRadians(horizontal, vertical); + SetFov( &fov, &fov ); + } + + + // This forces a "zero IPD" mode where there is just a single render with an FOV that + // is the union of the two calculated FOVs. + // The calculated render is for the left eye. Any size & FOV overrides for the right + // eye will be ignored. + // If you query the right eye's size, you will get the same render + // size & position as the left eye - you should not actually do the render of course! + // The distortion values will be different, because it goes to a different place on the framebuffer. + // Note that if you do this, the rendertarget does not need to be twice the width of + // the render size any more. + void SetZeroVirtualIpdOverride ( bool enableOverride ); + + // Allows the app to specify near and far clip planes and the right/left-handedness of the projection matrix. + void SetZClipPlanesAndHandedness ( float zNear = 0.01f, float zFar = 10000.0f, + bool rightHandedProjection = true ); + + // Allows the app to specify how much extra eye rotation to allow when determining the visible FOV. + void SetExtraEyeRotation ( float extraEyeRotationInRadians = 0.0f ); + + // The dirty flag is set by any of the above calls. Just handy for the app to know + // if e.g. the distortion mesh needs regeneration. + void SetDirty() { DirtyFlag = true; } + bool IsDirty() { return DirtyFlag; } + + // An app never needs to call this - GetEyeRenderParams will call it internally if + // the state is dirty. However apps can call this explicitly to control when and where + // computation is performed (e.g. not inside critical loops) + void UpdateComputedState(); + + // This returns the projection matrix with a "zoom". Does not modify any internal state. + Matrix4f GetProjectionWithZoom ( StereoEye eye, float fovZoom ) const; + + + //--------------------------------------------------------------------------------------------- + // The SetRender* functions are special. + // + // They do not require a full recalculation of state, and they do not change anything but the + // ViewportScaleAndOffset data for the eyes (which they return), and do not set the dirty flag! + // This means they can be called without regenerating the distortion mesh, and thus + // can happily be called every frame without causing performance problems. Dynamic rescaling + // of the rendertarget can help keep framerate up in demanding VR applications. + // See the documentation for more details on their use. + + // Specify a pixel density - how many rendered pixels per pixel in the physical display. + ViewportScaleAndOffsetBothEyes SetRenderDensity ( float pixelsPerDisplayPixel ); + + // Supply the size directly. Will be clamped to the physical rendertarget size. + ViewportScaleAndOffsetBothEyes SetRenderSize ( Sizei const &renderSizeLeft, Sizei const &renderSizeRight ); + + // Supply the viewport directly. This is not clamped to the physical rendertarget - careful now! + ViewportScaleAndOffsetBothEyes SetRenderViewport ( Recti const &renderViewportLeft, Recti const &renderViewportRight ); + +private: + + // *** Modifiable State + + StereoMode Mode; + HmdRenderInfo Hmd; + + float Area2DFov; // FOV range mapping to the 2D area. + + // Only one of these three overrides can be true! + enum SetViewportModeEnum + { + SVPM_Density, + SVPM_Size, + SVPM_Viewport, + } SetViewportMode; + // ...and depending which it is, one of the following are used. + float SetViewportPixelsPerDisplayPixel; + Sizei SetViewportSize[2]; + Recti SetViewport[2]; + + // Other overrides. + bool OverrideLens; + LensConfig LensOverrideLeft; + LensConfig LensOverrideRight; + Sizei RendertargetSize; + bool OverrideTanHalfFov; + FovPort FovOverrideLeft; + FovPort FovOverrideRight; + bool OverrideZeroIpd; + float ZNear; + float ZFar; + float ExtraEyeRotationInRadians; + bool IsRendertargetSharedByBothEyes; + bool RightHandedProjection; + + bool DirtyFlag; // Set when any if the modifiable state changed. Does NOT get set by SetRender*() + + // Utility function. + ViewportScaleAndOffsetBothEyes setupViewportScaleAndOffsets(); + + // *** Computed State + +public: // Small hack for the config tool. Normal code should never read EyeRenderParams directly - use GetEyeRenderParams() instead. + // 0/1 = left/right main views. + StereoEyeParamsWithOrtho EyeRenderParams[2]; +}; + + +//----------------------------------------------------------------------------------- +// ***** Distortion Mesh Rendering +// + +// Stores both texture UV coords, or tan(angle) values. +// Use whichever set of data the specific distortion algorithm requires. +// This struct *must* be binary compatible with CAPI ovrDistortionVertex. +struct DistortionMeshVertexData +{ + // [-1,+1],[-1,+1] over the entire framebuffer. + Vector2f ScreenPosNDC; + // [0.0-1.0] interpolation value for timewarping - see documentation for details. + float TimewarpLerp; + // [0.0-1.0] fade-to-black at the edges to reduce peripheral vision noise. + float Shade; + // The red, green, and blue vectors in tan(angle) space. + // Scale and offset by the values in StereoEyeParams.EyeToSourceUV.Scale + // and StereoParams.EyeToSourceUV.Offset to get to real texture UV coords. + Vector2f TanEyeAnglesR; + Vector2f TanEyeAnglesG; + Vector2f TanEyeAnglesB; +}; + + +void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, + int *pNumVertices, int *pNumTriangles, + const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ); + +// Generate distortion mesh for a eye. This version requires less data then stereoParms, supporting +// dynamic change in render target viewport. +void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, + int *pNumVertices, int *pNumTriangles, + bool rightEye, + const HmdRenderInfo &hmdRenderInfo, + const DistortionRenderDesc &distortion, const ScaleAndOffset2D &eyeToSourceNDC ); + +void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ); + + +//----------------------------------------------------------------------------------- +// ***** Heightmap Mesh Rendering +// + +// Stores both texture UV coords, or tan(angle) values. +// This struct *must* be binary compatible with CAPI ovrHeightmapVertex. +struct HeightmapMeshVertexData +{ + // [-1,+1],[-1,+1] over the entire framebuffer. + Vector2f ScreenPosNDC; + // [0.0-1.0] interpolation value for timewarping - see documentation for details. + float TimewarpLerp; + // The vectors in tan(angle) space. + // Scale and offset by the values in StereoEyeParams.EyeToSourceUV.Scale + // and StereoParams.EyeToSourceUV.Offset to get to real texture UV coords. + Vector2f TanEyeAngles; +}; + + +void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, + int *pNumVertices, int *pNumTriangles, + const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ); + +// Generate heightmap mesh for a eye. This version requires less data then stereoParms, supporting +// dynamic change in render target viewport. +void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, + int *pNumVertices, int *pNumTriangles, bool rightEye, + const HmdRenderInfo &hmdRenderInfo, const ScaleAndOffset2D &eyeToSourceNDC ); + +void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ); + + + +//----------------------------------------------------------------------------------- +// ***** Prediction and timewarp. +// + +struct PredictionValues +{ + // All values in seconds. + // These are the times in seconds from a present+flush to the relevant display element. + // The time is measured to the middle of that element's visibility window, + // e.g. if the device is a full-persistence display, the element will be visible for + // an entire frame, so the time measures to the middle of that period, i.e. half the frame time. + float PresentFlushToRenderedScene; // To the overall rendered 3D scene being visible. + float PresentFlushToTimewarpStart; // To when the first timewarped scanline will be visible. + float PresentFlushToTimewarpEnd; // To when the last timewarped scanline will be visible. + float PresentFlushToPresentFlush; // To the next present+flush, i.e. the ideal framerate. + + bool WithTimewarp; + bool WithVsync; +}; + +// Calculates the values from the HMD info. +PredictionValues PredictionGetDeviceValues ( const HmdRenderInfo &hmdRenderInfo, + bool withTimewarp = true, + bool withVsync = true ); + +// Pass in an orientation used to render the scene, and then the predicted orientation +// (which may have been computed later on, and thus is more accurate), and this +// will return the matrix to pass to the timewarp distortion shader. +// TODO: deal with different handedness? +Matrix4f TimewarpComputePoseDelta ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust ); +Matrix4f TimewarpComputePoseDeltaPosition ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust ); + + + +// TimewarpMachine helps keep track of rendered frame timing and +// handles predictions for time-warp rendering. +class TimewarpMachine +{ +public: + TimewarpMachine(); + + // Call this on and every time something about the setup changes. + void Reset ( HmdRenderInfo& renderInfo, bool vsyncEnabled, double timeNow ); + + // The only reliable time in most engines is directly after the frame-present and GPU flush-and-wait. + // This call should be done right after that to give this system the timing info it needs. + void AfterPresentAndFlush(double timeNow); + + // The "average" time the rendered frame will show up, + // and the predicted pose of the HMD at that time. + // You usually only need to call one of these functions. + double GetViewRenderPredictionTime(); + Transformf GetViewRenderPredictionPose(SensorFusion &sfusion); + + + // Timewarp prediction functions. You usually only need to call one of these three sets of functions. + + // The predicted times that the first and last pixel will be visible on-screen. + double GetVisiblePixelTimeStart(); + double GetVisiblePixelTimeEnd(); + // Predicted poses of the HMD at those first and last pixels. + Transformf GetPredictedVisiblePixelPoseStart(SensorFusion &sfusion); + Transformf GetPredictedVisiblePixelPoseEnd (SensorFusion &sfusion); + // The delta matrices to feed to the timewarp distortion code, + // given the pose that was used for rendering. + // (usually the one returned by GetViewRenderPredictionPose() earlier) + Matrix4f GetTimewarpDeltaStart(SensorFusion &sfusion, Transformf const &renderedPose); + Matrix4f GetTimewarpDeltaEnd (SensorFusion &sfusion, Transformf const &renderedPose); + + + // Just-In-Time distortion aims to delay the second sensor reading & distortion + // until the very last moment to improve prediction. However, it is a little scary, + // since the delay might wait too long and miss the vsync completely! + // Use of the JustInTime_* functions is entirely optional, and we advise allowing + // users to turn it off in their video options to cope with odd machine configurations. + + // What time should the app wait until before starting distortion? + double JustInTime_GetDistortionWaitUntilTime(); + + // Used to time the distortion rendering + bool JustInTime_NeedDistortionTimeMeasurement() const; + void JustInTime_BeforeDistortionTimeMeasurement(double timeNow); + void JustInTime_AfterDistortionTimeMeasurement(double timeNow); + + +private: + + bool VsyncEnabled; + HmdRenderInfo RenderInfo; + PredictionValues CurrentPredictionValues; + + enum { NumDistortionTimes = 10 }; + int DistortionTimeCount; + double DistortionTimeCurrentStart; + float DistortionTimes[NumDistortionTimes]; + float DistortionTimeAverage; + + // Pose at which last time the eye was rendered. + Transformf EyeRenderPoses[2]; + + // Absolute time of the last present+flush + double LastFramePresentFlushTime; + // Seconds between presentflushes + float PresentFlushToPresentFlushSeconds; + // Predicted absolute time of the next present+flush + double NextFramePresentFlushTime; + +}; + + + +}}} // OVR::Util::Render + +#endif |