nuclear@3: /************************************************************************************ nuclear@3: nuclear@3: PublicHeader: OVR.h nuclear@3: Filename : Util_Render_Stereo.h nuclear@3: Content : Sample stereo rendering configuration classes. nuclear@3: Created : October 22, 2012 nuclear@3: Authors : Michael Antonov nuclear@3: nuclear@3: Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. nuclear@3: nuclear@3: Use of this software is subject to the terms of the Oculus Inc license nuclear@3: agreement provided at the time of installation or download, or which nuclear@3: otherwise accompanies this software in either electronic or hard copy form. nuclear@3: nuclear@3: *************************************************************************************/ nuclear@3: nuclear@3: #ifndef OVR_Util_Render_Stereo_h nuclear@3: #define OVR_Util_Render_Stereo_h nuclear@3: nuclear@3: #include "../OVR_Device.h" nuclear@3: nuclear@3: namespace OVR { namespace Util { namespace Render { nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Stereo Enumerations nuclear@3: nuclear@3: // StereoMode describes rendering modes that can be used by StereoConfig. nuclear@3: // These modes control whether stereo rendering is used or not (Stereo_None), nuclear@3: // and how it is implemented. nuclear@3: enum StereoMode nuclear@3: { nuclear@3: Stereo_None = 0, nuclear@3: Stereo_LeftRight_Multipass = 1 nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: // StereoEye specifies which eye we are rendering for; it is used to nuclear@3: // retrieve StereoEyeParams. nuclear@3: enum StereoEye nuclear@3: { nuclear@3: StereoEye_Center, nuclear@3: StereoEye_Left, nuclear@3: StereoEye_Right nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Viewport nuclear@3: nuclear@3: // Viewport describes a rectangular area used for rendering, in pixels. nuclear@3: struct Viewport nuclear@3: { nuclear@3: int x, y; nuclear@3: int w, h; nuclear@3: nuclear@3: Viewport() {} nuclear@3: Viewport(int x1, int y1, int w1, int h1) : x(x1), y(y1), w(w1), h(h1) { } nuclear@3: nuclear@3: bool operator == (const Viewport& vp) const nuclear@3: { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); } nuclear@3: bool operator != (const Viewport& vp) const nuclear@3: { return !operator == (vp); } nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** DistortionConfig nuclear@3: nuclear@3: // DistortionConfig Provides controls for the distortion shader. nuclear@3: // - K[0] - K[3] are coefficients for the distortion function. nuclear@3: // - XCenterOffset is the offset of lens distortion center from the nuclear@3: // center of one-eye screen half. [-1, 1] Range. nuclear@3: // - Scale is a factor of how much larger will the input image be, nuclear@3: // with a factor of 1.0f being no scaling. An inverse of this nuclear@3: // value is applied to sampled UV coordinates (1/Scale). nuclear@3: // - ChromaticAberration is an array of parameters for controlling nuclear@3: // additional Red and Blue scaling in order to reduce chromatic aberration nuclear@3: // caused by the Rift lenses. nuclear@3: class DistortionConfig nuclear@3: { nuclear@3: public: nuclear@3: DistortionConfig(float k0 = 1.0f, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) nuclear@3: : XCenterOffset(0), YCenterOffset(0), Scale(1.0f) nuclear@3: { nuclear@3: SetCoefficients(k0, k1, k2, k3); nuclear@3: SetChromaticAberration(); nuclear@3: } nuclear@3: nuclear@3: void SetCoefficients(float k0, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) nuclear@3: { K[0] = k0; K[1] = k1; K[2] = k2; K[3] = k3; } nuclear@3: nuclear@3: void SetChromaticAberration(float red1 = 1.0f, float red2 = 0.0f, float blue1 = 1.0f, float blue2 = 0.0f) nuclear@3: { ChromaticAberration[0] = red1; ChromaticAberration[1] = red2; ChromaticAberration[2] = blue1; ChromaticAberration[3] = blue2; } nuclear@3: nuclear@3: nuclear@3: // DistortionFn applies distortion equation to the argument. The returned nuclear@3: // value should match distortion equation used in shader. nuclear@3: float DistortionFn(float r) const nuclear@3: { nuclear@3: float rsq = r * r; nuclear@3: float scale = r * (K[0] + K[1] * rsq + K[2] * rsq * rsq + K[3] * rsq * rsq * rsq); nuclear@3: return scale; nuclear@3: } nuclear@3: nuclear@3: // DistortionFnInverse computes the inverse of the distortion function on an argument. nuclear@3: float DistortionFnInverse(float r); nuclear@3: nuclear@3: float K[4]; nuclear@3: float XCenterOffset, YCenterOffset; nuclear@3: float Scale; nuclear@3: nuclear@3: float ChromaticAberration[4]; // Additional per-channel scaling is applied after distortion: nuclear@3: // Index [0] - Red channel constant coefficient. nuclear@3: // Index [1] - Red channel r^2 coefficient. nuclear@3: // Index [2] - Blue channel constant coefficient. nuclear@3: // Index [3] - Blue channel r^2 coefficient. nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** StereoEyeParams nuclear@3: nuclear@3: // StereoEyeParams describes RenderDevice configuration needed to render nuclear@3: // the scene for one eye. nuclear@3: class StereoEyeParams nuclear@3: { nuclear@3: public: nuclear@3: StereoEye Eye; nuclear@3: Viewport VP; // Viewport that we are rendering to nuclear@3: const DistortionConfig* pDistortion; nuclear@3: nuclear@3: Matrix4f ViewAdjust; // Translation to be applied to view matrix. nuclear@3: Matrix4f Projection; // Projection matrix used with this eye. nuclear@3: Matrix4f OrthoProjection; // Orthographic projection used with this eye. nuclear@3: nuclear@3: void Init(StereoEye eye, const Viewport &vp, float vofs, nuclear@3: const Matrix4f& proj, const Matrix4f& orthoProj, nuclear@3: const DistortionConfig* distortion = 0) nuclear@3: { nuclear@3: Eye = eye; nuclear@3: VP = vp; nuclear@3: ViewAdjust = Matrix4f::Translation(Vector3f(vofs,0,0)); nuclear@3: Projection = proj; nuclear@3: OrthoProjection = orthoProj; nuclear@3: pDistortion = distortion; nuclear@3: } nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** StereoConfig nuclear@3: nuclear@3: // StereoConfig maintains a scene stereo state and allow switching between different nuclear@3: // stereo rendering modes. To support rendering, StereoConfig keeps track of HMD nuclear@3: // variables such as screen size, eye-to-screen distance and distortion, and computes nuclear@3: // extra data such as FOV and distortion center offsets based on it. Rendering nuclear@3: // parameters are returned though StereoEyeParams for each eye. nuclear@3: // nuclear@3: // Beyond regular 3D projection, this class supports rendering a 2D orthographic nuclear@3: // surface for UI and text. The 2D surface will be defined as fitting within a 2D nuclear@3: // field of view (85 degrees by default) and used [-1,1] coordinate system with nuclear@3: // square pixels. The (0,0) coordinate corresponds to eye center location nuclear@3: // that is properly adjusted during rendering through SterepRenderParams::Adjust2D. nuclear@3: // Genreally speaking, text outside [-1,1] coordinate range will not be readable. nuclear@3: nuclear@3: class StereoConfig nuclear@3: { nuclear@3: public: nuclear@3: nuclear@3: StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass, nuclear@3: const Viewport& fullViewport = Viewport(0,0, 1280,800)); nuclear@3: nuclear@3: nuclear@3: // *** Modifiable State Access nuclear@3: nuclear@3: // Sets a stereo rendering mode and updates internal cached nuclear@3: // state (matrices, per-eye view) based on it. nuclear@3: void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; } nuclear@3: StereoMode GetStereoMode() const { return Mode; } nuclear@3: nuclear@3: // Sets HMD parameters; also initializes distortion coefficients. nuclear@3: void SetHMDInfo(const HMDInfo& hmd); nuclear@3: const HMDInfo& GetHMDInfo() const { return HMD; } nuclear@3: nuclear@3: // Query physical eye-to-screen distance in meters, which combines screen-to-lens and nuclear@3: // and lens-to-eye pupil distances. Modifying this value adjusts FOV. nuclear@3: float GetEyeToScreenDistance() const { return HMD.EyeToScreenDistance; } nuclear@3: void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; } nuclear@3: nuclear@3: // Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm). nuclear@3: void SetIPD(float ipd) { InterpupillaryDistance = ipd; IPDOverride = DirtyFlag = true; } nuclear@3: float GetIPD() const { return InterpupillaryDistance; } nuclear@3: nuclear@3: // Set full render target viewport; for HMD this includes both eyes. nuclear@3: void SetFullViewport(const Viewport& vp); nuclear@3: const Viewport& GetFullViewport() const { return FullView; } nuclear@3: nuclear@3: // Aspect ratio defaults to ((w/h)*multiplier) computed per eye. nuclear@3: // Aspect multiplier allows adjusting aspect ratio consistently for Stereo/NoStereo. nuclear@3: void SetAspectMultiplier(float m) { AspectMultiplier = m; DirtyFlag = true; } nuclear@3: float GetAspectMultiplier() const { return AspectMultiplier; } nuclear@3: nuclear@3: nuclear@3: // For the distorted image to fill rendered viewport, input texture render target needs to be nuclear@3: // scaled by DistortionScale before sampling. The scale factor is computed by fitting a point nuclear@3: // on of specified radius from a distortion center, more easily specified as a coordinate. nuclear@3: // SetDistortionFitPointVP sets the (x,y) coordinate of the point that scale will be "fit" to, nuclear@3: // assuming [-1,1] coordinate range for full left-eye viewport. A fit point is a location nuclear@3: // where source (pre-distortion) and target (post-distortion) image match each other. nuclear@3: // For the right eye, the interpretation of 'u' will be inverted. nuclear@3: void SetDistortionFitPointVP(float x, float y); nuclear@3: // SetDistortionFitPointPixels sets the (x,y) coordinate of the point that scale will be "fit" to, nuclear@3: // specified in pixeld for full left-eye texture. nuclear@3: void SetDistortionFitPointPixels(float x, float y); nuclear@3: nuclear@3: // Changes all distortion settings. nuclear@3: // Note that setting HMDInfo also changes Distortion coefficients. nuclear@3: void SetDistortionConfig(const DistortionConfig& d) { Distortion = d; DirtyFlag = true; } nuclear@3: nuclear@3: // Modify distortion coefficients; useful for adjustment tweaking. nuclear@3: void SetDistortionK(int i, float k) { Distortion.K[i] = k; DirtyFlag = true; } nuclear@3: float GetDistortionK(int i) const { return Distortion.K[i]; } nuclear@3: nuclear@3: // Sets the fieldOfView that the 2D coordinate area stretches to. nuclear@3: void Set2DAreaFov(float fovRadians); nuclear@3: nuclear@3: nuclear@3: // *** Computed State nuclear@3: nuclear@3: // Return current aspect ratio. nuclear@3: float GetAspect() { updateIfDirty(); return Aspect; } nuclear@3: nuclear@3: // Return computed vertical FOV in radians/degrees. nuclear@3: float GetYFOVRadians() { updateIfDirty(); return YFov; } nuclear@3: float GetYFOVDegrees() { return RadToDegree(GetYFOVRadians()); } nuclear@3: nuclear@3: // Query horizontal projection center offset as a distance away from the nuclear@3: // one-eye [-1,1] unit viewport. nuclear@3: // Positive return value should be used for left eye, negative for right eye. nuclear@3: float GetProjectionCenterOffset() { updateIfDirty(); return ProjectionCenterOffset; } nuclear@3: nuclear@3: // GetDistortionConfig isn't const because XCenterOffset bay need to be recomputed. nuclear@3: const DistortionConfig& GetDistortionConfig() { updateIfDirty(); return Distortion; } nuclear@3: nuclear@3: // Returns DistortionScale factor by which input texture size is increased to make nuclear@3: // post-distortion result distortion fit the viewport. nuclear@3: float GetDistortionScale() { updateIfDirty(); return Distortion.Scale; } nuclear@3: nuclear@3: // Returns the size of a pixel within 2D coordinate system. nuclear@3: float Get2DUnitPixel() { updateIfDirty(); return (2.0f / (FovPixels * Distortion.Scale)); } nuclear@3: nuclear@3: // Returns full set of Stereo rendering parameters for the specified eye. nuclear@3: const StereoEyeParams& GetEyeRenderParams(StereoEye eye); nuclear@3: nuclear@3: private: nuclear@3: nuclear@3: void updateIfDirty() { if (DirtyFlag) updateComputedState(); } nuclear@3: void updateComputedState(); nuclear@3: nuclear@3: void updateDistortionOffsetAndScale(); nuclear@3: void updateProjectionOffset(); nuclear@3: void update2D(); nuclear@3: void updateEyeParams(); nuclear@3: nuclear@3: nuclear@3: // *** Modifiable State nuclear@3: nuclear@3: StereoMode Mode; nuclear@3: float InterpupillaryDistance; nuclear@3: float AspectMultiplier; // Multiplied into aspect ratio to change it. nuclear@3: HMDInfo HMD; nuclear@3: DistortionConfig Distortion; nuclear@3: float DistortionFitX, DistortionFitY; // In [-1,1] half-screen viewport units. nuclear@3: Viewport FullView; // Entire window viewport. nuclear@3: nuclear@3: float Area2DFov; // FOV range mapping to [-1, 1] 2D area. nuclear@3: nuclear@3: // *** Computed State nuclear@3: nuclear@3: bool DirtyFlag; // Set when any if the modifiable state changed. nuclear@3: bool IPDOverride; // True after SetIPD was called. nuclear@3: float YFov; // Vertical FOV. nuclear@3: float Aspect; // Aspect ratio: (w/h)*AspectMultiplier. nuclear@3: float ProjectionCenterOffset; nuclear@3: StereoEyeParams EyeRenderParams[2]; nuclear@3: nuclear@3: nuclear@3: // ** 2D Rendering nuclear@3: nuclear@3: // Number of 2D pixels in the FOV. This defines [-1,1] coordinate range for 2D. nuclear@3: float FovPixels; nuclear@3: Matrix4f OrthoCenter; nuclear@3: float OrthoPixelOffset; nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: }}} // OVR::Util::Render nuclear@3: nuclear@3: #endif