From: Mikko Rasa Date: Tue, 23 Dec 2014 21:41:02 +0000 (+0200) Subject: Add support for time warp on Oculus Rift X-Git-Url: http://git.tdb.fi/?p=libs%2Fvr.git;a=commitdiff_plain;h=ed0489e44b7cb9748e5086e1e78ef65e1c3d1930 Add support for time warp on Oculus Rift To make use of it, the program must call device->begin_frame() before it starts rendering and device->end_frame just after swap_buffers. --- diff --git a/source/displaydevice.h b/source/displaydevice.h index b309a83..81a9d24 100644 --- a/source/displaydevice.h +++ b/source/displaydevice.h @@ -20,6 +20,9 @@ public: virtual void configure_view(StereoView &) const = 0; virtual HeadTrackingCamera *create_camera(const GL::Camera &) const = 0; virtual StereoCombiner *create_combiner() const = 0; + + virtual void begin_frame() = 0; + virtual void end_frame() = 0; }; } // namespace VR diff --git a/source/oculusriftcamera.cpp b/source/oculusriftcamera.cpp index 3aa35be..0eede27 100644 --- a/source/oculusriftcamera.cpp +++ b/source/oculusriftcamera.cpp @@ -20,7 +20,13 @@ void OculusRiftCamera::reset_tracking() void OculusRiftCamera::update() { - ovrTrackingState state = ovrHmd_GetTrackingState(device.get_private().ovr_hmd, ovr_GetTimeInSeconds()); + double time; + if(device.is_timing_active()) + time = device.get_tracking_time(); + else + time = device.get_current_time(); + + ovrTrackingState state = ovrHmd_GetTrackingState(device.get_private().ovr_hmd, time); OVR::Posef head_pose = state.HeadPose.ThePose; OVR::Matrix4f tracking_matrix(head_pose.Rotation); OVR::Vector3f trans = head_pose.Translation; diff --git a/source/oculusriftcombiner.cpp b/source/oculusriftcombiner.cpp index 5e76cc8..0cfd4ae 100644 --- a/source/oculusriftcombiner.cpp +++ b/source/oculusriftcombiner.cpp @@ -11,16 +11,26 @@ using namespace std; namespace { const char vs_source[] = + "uniform mat4 timewarp[2];\n" + "uniform vec2 uv_scale;\n" + "uniform vec2 uv_offset;\n" "varying vec2 texcoord_r;\n" "varying vec2 texcoord_g;\n" "varying vec2 texcoord_b;\n" "varying float vignette;\n" + "vec2 apply_timewarp(vec2 coords, float factor)\n" + "{\n" + " vec4 coords4 = vec4(coords, 1.0, 1.0);\n" + " vec4 warped = mix(timewarp[0]*coords4, timewarp[1]*coords4, factor);\n" + " return (warped.xy/warped.z)*uv_scale+uv_offset;\n" + "}\n" "void main()\n" "{\n" " gl_Position = vec4(gl_Vertex.xy, 0.5, 1.0);\n" - " texcoord_r = gl_MultiTexCoord0.xy;\n" - " texcoord_g = gl_MultiTexCoord1.xy;\n" - " texcoord_b = gl_MultiTexCoord2.xy;\n" + " float tw_factor = gl_MultiTexCoord3.y;\n" + " texcoord_r = apply_timewarp(gl_MultiTexCoord0.xy, tw_factor);\n" + " texcoord_g = apply_timewarp(gl_MultiTexCoord1.xy, tw_factor);\n" + " texcoord_b = apply_timewarp(gl_MultiTexCoord2.xy, tw_factor);\n" " vignette = gl_MultiTexCoord3.x;\n" "}\n"; @@ -41,26 +51,16 @@ const char fs_source[] = void create_distortion_mesh(Msp::GL::Mesh &mesh, ovrHmd hmd, ovrEyeType eye, const ovrFovPort &fov) { ovrDistortionMesh ovr_mesh; - ovrHmd_CreateDistortionMesh(hmd, eye, fov, ovrDistortionCap_Chromatic|ovrDistortionCap_Vignette, &ovr_mesh); - - ovrSizei tex_size = ovrHmd_GetFovTextureSize(hmd, eye, fov, 1.0); - ovrRecti view_rect; - view_rect.Pos.x = 0; - view_rect.Pos.y = 0; - view_rect.Size = tex_size; - ovrVector2f uv_scale_offset[2]; - ovrHmd_GetRenderScaleAndOffset(fov, tex_size, view_rect, uv_scale_offset); - ovrVector2f &scale = uv_scale_offset[0]; - ovrVector2f &offset = uv_scale_offset[1]; + ovrHmd_CreateDistortionMesh(hmd, eye, fov, ovrDistortionCap_Chromatic|ovrDistortionCap_Vignette|ovrDistortionCap_TimeWarp, &ovr_mesh); Msp::GL::MeshBuilder bld(mesh); for(unsigned i=0; iResolution.h/hmd->Resolution.w; - shdata.uniform("texture", 0); + left_shdata.uniform("texture", 0); + right_shdata.uniform("texture", 0); + ovrRecti view_rect; + view_rect.Pos.x = 0; + view_rect.Pos.y = 0; + view_rect.Size = tex_size; + ovrVector2f uv_scale_offset[2]; + ovrHmd_GetRenderScaleAndOffset(left_fov, tex_size, view_rect, uv_scale_offset); + left_shdata.uniform("uv_scale", uv_scale_offset[0].x, -uv_scale_offset[0].y); + left_shdata.uniform("uv_offset", uv_scale_offset[1].x, 1-uv_scale_offset[1].y); + ovrHmd_GetRenderScaleAndOffset(right_fov, tex_size, view_rect, uv_scale_offset); + right_shdata.uniform("uv_scale", uv_scale_offset[0].x, -uv_scale_offset[0].y); + right_shdata.uniform("uv_offset", uv_scale_offset[1].x, 1-uv_scale_offset[1].y); } void OculusRiftCombiner::render(const GL::Texture2D &left, const GL::Texture2D &right) const { GL::Bind bind_shprog(shprog); - shdata.apply(); + + ovrHmd hmd = device.get_private().ovr_hmd; + + if(device.is_timing_active()) + { + ovr_WaitTillTime(device.get_timewarp_time()); + ovrTrackingState state = ovrHmd_GetTrackingState(hmd, device.get_tracking_time()); + + ovrMatrix4f matrices[2]; + ovrHmd_GetEyeTimewarpMatrices(hmd, ovrEye_Left, state.HeadPose.ThePose, matrices); + left_shdata.uniform_matrix4_array("timewarp", 2, &matrices[0].M[0][0]); + + ovrHmd_GetEyeTimewarpMatrices(hmd, ovrEye_Right, state.HeadPose.ThePose, matrices); + right_shdata.uniform_matrix4_array("timewarp", 2, &matrices[0].M[0][0]); + } + else + { + GL::Matrix matrices[2]; + left_shdata.uniform_matrix4_array("timewarp", 2, matrices[0].data()); + right_shdata.uniform_matrix4_array("timewarp", 2, matrices[0].data()); + } GL::Bind bind_tex(left); + left_shdata.apply(); left_mesh.draw(); right.bind(); + right_shdata.apply(); right_mesh.draw(); } diff --git a/source/oculusriftcombiner.h b/source/oculusriftcombiner.h index 435ccb3..32ab9cc 100644 --- a/source/oculusriftcombiner.h +++ b/source/oculusriftcombiner.h @@ -22,7 +22,8 @@ private: GL::Mesh left_mesh; GL::Mesh right_mesh; GL::Program shprog; - GL::ProgramData shdata; + mutable GL::ProgramData left_shdata; + mutable GL::ProgramData right_shdata; public: OculusRiftCombiner(const OculusRiftDevice &); diff --git a/source/oculusriftdevice.cpp b/source/oculusriftdevice.cpp index f521a0e..f73371f 100644 --- a/source/oculusriftdevice.cpp +++ b/source/oculusriftdevice.cpp @@ -10,7 +10,8 @@ namespace VR { unsigned OculusRiftDevice::n_instances = 0; OculusRiftDevice::OculusRiftDevice(): - priv(new Private) + priv(new Private), + frame_index(0) { if(!n_instances) ovr_Initialize(); @@ -51,5 +52,37 @@ OculusRiftCombiner *OculusRiftDevice::create_combiner() const return new OculusRiftCombiner(*this); } +void OculusRiftDevice::begin_frame() +{ + priv->frame_timing = ovrHmd_BeginFrameTiming(priv->ovr_hmd, ++frame_index); + timing_active = true; +} + +void OculusRiftDevice::end_frame() +{ + glFinish(); + ovrHmd_EndFrameTiming(priv->ovr_hmd); + timing_active = false; +} + +double OculusRiftDevice::get_tracking_time() const +{ + if(!timing_active) + throw logic_error("timing not active"); + return priv->frame_timing.ScanoutMidpointSeconds; +} + +double OculusRiftDevice::get_timewarp_time() const +{ + if(!timing_active) + throw logic_error("timing not active"); + return priv->frame_timing.TimewarpPointSeconds; +} + +double OculusRiftDevice::get_current_time() const +{ + return ovr_GetTimeInSeconds(); +} + } // namespace VR } // namespace Msp diff --git a/source/oculusriftdevice.h b/source/oculusriftdevice.h index ecff70e..9eee6e3 100644 --- a/source/oculusriftdevice.h +++ b/source/oculusriftdevice.h @@ -15,6 +15,8 @@ public: private: Private *priv; + unsigned frame_index; + bool timing_active; static unsigned n_instances; @@ -27,6 +29,13 @@ public: virtual void configure_view(StereoView &) const; virtual OculusRiftCamera *create_camera(const GL::Camera &) const; virtual OculusRiftCombiner *create_combiner() const; + + virtual void begin_frame(); + virtual void end_frame(); + bool is_timing_active() const { return timing_active; } + double get_tracking_time() const; + double get_timewarp_time() const; + double get_current_time() const; }; } // namespace VR diff --git a/source/oculusriftdevice_private.h b/source/oculusriftdevice_private.h index 2b9c695..26587ff 100644 --- a/source/oculusriftdevice_private.h +++ b/source/oculusriftdevice_private.h @@ -10,6 +10,7 @@ namespace VR { struct OculusRiftDevice::Private { ovrHmd ovr_hmd; + ovrFrameTiming frame_timing; }; } // namespace VR