From 43579020c5709b589b2404414c489b0e6be3227d Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 18 Jun 2013 18:11:20 +0300 Subject: [PATCH] Add classes for stereographic rendering --- source/sidebysidecombiner.cpp | 71 ++++++++++++++++++++ source/sidebysidecombiner.h | 32 +++++++++ source/stereocombiner.cpp | 14 ++++ source/stereocombiner.h | 32 +++++++++ source/stereoview.cpp | 120 ++++++++++++++++++++++++++++++++++ source/stereoview.h | 73 +++++++++++++++++++++ 6 files changed, 342 insertions(+) create mode 100644 source/sidebysidecombiner.cpp create mode 100644 source/sidebysidecombiner.h create mode 100644 source/stereocombiner.cpp create mode 100644 source/stereocombiner.h create mode 100644 source/stereoview.cpp create mode 100644 source/stereoview.h diff --git a/source/sidebysidecombiner.cpp b/source/sidebysidecombiner.cpp new file mode 100644 index 0000000..b9d1d89 --- /dev/null +++ b/source/sidebysidecombiner.cpp @@ -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 index 0000000..f6ec593 --- /dev/null +++ b/source/sidebysidecombiner.h @@ -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 index 0000000..27cbb8f --- /dev/null +++ b/source/stereocombiner.cpp @@ -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 index 0000000..1017930 --- /dev/null +++ b/source/stereocombiner.h @@ -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 index 0000000..9a4b0e0 --- /dev/null +++ b/source/stereoview.cpp @@ -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 ¶ms) 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 index 0000000..63ee183 --- /dev/null +++ b/source/stereoview.h @@ -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 -- 2.43.0