Add classes for stereographic rendering
authorMikko Rasa <tdb@tdb.fi>
Tue, 18 Jun 2013 15:11:20 +0000 (18:11 +0300)
committerMikko Rasa <tdb@tdb.fi>
Fri, 13 Sep 2013 11:31:05 +0000 (14:31 +0300)
source/sidebysidecombiner.cpp [new file with mode: 0644]
source/sidebysidecombiner.h [new file with mode: 0644]
source/stereocombiner.cpp [new file with mode: 0644]
source/stereocombiner.h [new file with mode: 0644]
source/stereoview.cpp [new file with mode: 0644]
source/stereoview.h [new file with mode: 0644]

diff --git a/source/sidebysidecombiner.cpp b/source/sidebysidecombiner.cpp
new file mode 100644 (file)
index 0000000..b9d1d89
--- /dev/null
@@ -0,0 +1,71 @@
+#include "meshbuilder.h"
+#include "sidebysidecombiner.h"
+#include "texture2d.h"
+
+namespace {
+
+const char vs_source[] =
+       "uniform float offset;\n"
+       "varying vec2 texcoord;\n"
+       "void main()\n"
+       "{\n"
+       "       gl_Position = vec4(gl_Vertex.x*0.5+offset, gl_Vertex.yzw);\n"
+       "       texcoord = gl_Vertex.xy*0.5+0.5;\n"
+       "}\n";
+
+const char fs_source[] =
+       "uniform sampler2D texture;\n"
+       "varying vec2 texcoord;\n"
+       "void main()\n"
+       "{\n"
+       "       gl_FragColor = texture2D(texture, texcoord);\n"
+       "}\n";
+
+}
+
+namespace Msp {
+namespace GL {
+
+SideBySideCombiner::SideBySideCombiner(bool c):
+       mesh(VERTEX2),
+       shprog(vs_source, fs_source)
+{
+       width_div = 2;
+
+       left_shdata.uniform("texture", 0);
+       right_shdata.uniform("texture", 0);
+
+       set_cross_eyed(c);
+
+       MeshBuilder bld(mesh);
+       bld.begin(TRIANGLE_STRIP);
+       bld.vertex(-1, 1);
+       bld.vertex(-1, -1);
+       bld.vertex(1, 1);
+       bld.vertex(1, -1);
+       bld.end();
+}
+
+void SideBySideCombiner::set_cross_eyed(bool c)
+{
+       cross_eyed = c;
+       float m = (cross_eyed ? -0.5f : 0.5f);
+       left_shdata.uniform("offset", -m);
+       right_shdata.uniform("offset", m);
+}
+
+void SideBySideCombiner::render(const Texture2D &left, const Texture2D &right) const
+{
+       Bind bind_shprog(shprog);
+
+       Bind bind_tex(left);
+       left_shdata.apply();
+       mesh.draw();
+
+       right.bind();
+       right_shdata.apply();
+       mesh.draw();
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/sidebysidecombiner.h b/source/sidebysidecombiner.h
new file mode 100644 (file)
index 0000000..f6ec593
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef MSP_GL_SIDEBYSIDECOMBINER_H_
+#define MSP_GL_SIDEBYSIDECOMBINER_H_
+
+#include "mesh.h"
+#include "program.h"
+#include "programdata.h"
+#include "stereocombiner.h"
+
+namespace Msp {
+namespace GL {
+
+class SideBySideCombiner: public StereoCombiner
+{
+private:
+       Mesh mesh;
+       Program shprog;
+       ProgramData left_shdata;
+       ProgramData right_shdata;
+       bool cross_eyed;
+
+public:
+       SideBySideCombiner(bool = false);
+
+       void set_cross_eyed(bool);
+
+       virtual void render(const Texture2D &, const Texture2D &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/stereocombiner.cpp b/source/stereocombiner.cpp
new file mode 100644 (file)
index 0000000..27cbb8f
--- /dev/null
@@ -0,0 +1,14 @@
+#include "stereocombiner.h"
+
+namespace Msp {
+namespace GL {
+
+StereoCombiner::StereoCombiner():
+       width_div(1),
+       height_div(1),
+       keep_aspect(false),
+       fov(0)
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/stereocombiner.h b/source/stereocombiner.h
new file mode 100644 (file)
index 0000000..1017930
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef MSP_GL_STEREOCOMBINER_H_
+#define MSP_GL_STEREOCOMBINER_H_
+
+namespace Msp {
+namespace GL {
+
+class Texture2D;
+
+class StereoCombiner
+{
+protected:
+       unsigned width_div;
+       unsigned height_div;
+       bool keep_aspect;
+       float fov;
+
+       StereoCombiner();
+public:
+       virtual ~StereoCombiner() { }
+
+       unsigned get_width_divisor() const { return width_div; }
+       unsigned get_height_divisor() const { return height_div; }
+       bool is_aspect_kept() const { return keep_aspect; }
+       float get_field_of_view() const { return fov; }
+
+       virtual void render(const Texture2D &, const Texture2D &) const = 0;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/stereoview.cpp b/source/stereoview.cpp
new file mode 100644 (file)
index 0000000..9a4b0e0
--- /dev/null
@@ -0,0 +1,120 @@
+#include "renderer.h"
+#include "stereocombiner.h"
+#include "stereoview.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+StereoView::StereoView(unsigned w, unsigned h, const Camera &c, const Renderable &r, const StereoCombiner &m):
+       width(w),
+       height(h),
+       base_camera(c),
+       renderable(r),
+       combiner(0)
+{
+       set_combiner(m);
+       set_eye_spacing(0.07);
+}
+
+void StereoView::set_combiner(const StereoCombiner &c)
+{
+       combiner = &c;
+
+       unsigned w = width/combiner->get_width_divisor();
+       unsigned h = height/combiner->get_height_divisor();
+       left.create_target(w, h);
+       right.create_target(w, h);
+}
+
+void StereoView::set_eye_spacing(float s)
+{
+       eye_spacing = s;
+}
+
+void StereoView::setup_frame() const
+{
+       offset_axis = normalize(cross(base_camera.get_look_direction(), base_camera.get_up_direction()))*0.5f;
+
+       EyeParams params;
+       params.fov = combiner->get_field_of_view();
+       if(!params.fov)
+               params.fov = base_camera.get_field_of_view();
+
+       params.aspect = base_camera.get_aspect();
+       if(!combiner->is_aspect_kept())
+               params.aspect = params.aspect*combiner->get_height_divisor()/combiner->get_width_divisor();
+
+       params.near_clip = base_camera.get_near_clip();
+       params.far_clip = base_camera.get_far_clip();
+
+       left.setup_frame(base_camera, offset_axis*-eye_spacing, params);
+       right.setup_frame(base_camera, offset_axis*eye_spacing, params);
+
+       renderable.setup_frame();
+}
+
+void StereoView::finish_frame() const
+{
+       renderable.finish_frame();
+}
+
+void StereoView::render(const Tag &tag) const
+{
+       setup_frame();
+       left.render(renderable, tag);
+       right.render(renderable, tag);
+       combiner->render(left.target->color, right.target->color);
+       finish_frame();
+}
+
+void StereoView::render(Renderer &renderer, const Tag &tag) const
+{
+       renderer.escape();
+       return render(tag);
+}
+
+
+StereoView::RenderTarget::RenderTarget(unsigned width, unsigned height)
+{
+       color.set_min_filter(LINEAR);
+       color.set_wrap(CLAMP_TO_EDGE);
+       color.storage(RGB, width, height);
+       fbo.attach(COLOR_ATTACHMENT0, color);
+
+       depth.storage(DEPTH_COMPONENT, width, height);
+       fbo.attach(DEPTH_ATTACHMENT, depth);
+}
+
+
+StereoView::Eye::Eye():
+       target(0)
+{ }
+
+void StereoView::Eye::create_target(unsigned w, unsigned h)
+{
+       delete target;
+       target = new RenderTarget(w, h);
+}
+
+void StereoView::Eye::setup_frame(const Camera &base_camera, const Vector3 &offset, const EyeParams &params) const
+{
+       camera.set_position(base_camera.get_position()+offset);
+       camera.set_up_direction(base_camera.get_up_direction());
+       camera.set_look_direction(base_camera.get_look_direction());
+
+       camera.set_field_of_view(params.fov);
+       camera.set_aspect(params.aspect);
+       camera.set_depth_clip(params.near_clip, params.far_clip);
+}
+
+void StereoView::Eye::render(const Renderable &renderable, const Tag &tag) const
+{
+       Bind bind_fbo(target->fbo);
+       Renderer renderer(&camera);
+       renderable.render(renderer, tag);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/stereoview.h b/source/stereoview.h
new file mode 100644 (file)
index 0000000..63ee183
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef MSP_GL_STEREOVIEW_H_
+#define MSP_GL_STEREOVIEW_H_
+
+#include "camera.h"
+#include "framebuffer.h"
+#include "renderable.h"
+#include "renderbuffer.h"
+#include "texture2d.h"
+
+namespace Msp {
+namespace GL {
+
+class StereoCombiner;
+
+class StereoView: public Renderable
+{
+private:
+       struct RenderTarget
+       {
+               Framebuffer fbo;
+               Texture2D color;
+               Renderbuffer depth;
+
+               RenderTarget(unsigned, unsigned);
+       };
+
+       struct EyeParams
+       {
+               float fov;
+               float aspect;
+               float near_clip;
+               float far_clip;
+       };
+
+       struct Eye
+       {
+               mutable Camera camera;
+               RenderTarget *target;
+
+               Eye();
+
+               void create_target(unsigned, unsigned);
+               void setup_frame(const Camera &, const Vector3 &, const EyeParams &) const;
+               void render(const Renderable &, const Tag &) const;
+       };
+
+       unsigned width;
+       unsigned height;
+       const Camera &base_camera;
+       const Renderable &renderable;
+       const StereoCombiner *combiner;
+       Eye left;
+       Eye right;
+       float eye_spacing;
+       mutable Vector3 offset_axis;
+
+public:
+       StereoView(unsigned, unsigned, const Camera &, const Renderable &, const StereoCombiner &);
+
+       void set_combiner(const StereoCombiner &);
+       void set_eye_spacing(float);
+
+       virtual void setup_frame() const;
+       virtual void finish_frame() const;
+
+       virtual void render(const Tag & = Tag()) const;
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif