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