b15c86be54a41e6e3d54b82702bd945b605e1bf3
[libs/vr.git] / source / oculusriftcombiner.cpp
1 #include <cmath>
2 #include <msp/gl/meshbuilder.h>
3 #include <msp/gl/texture2d.h>
4 #include "oculusriftcombiner.h"
5
6 using namespace std;
7
8 namespace {
9
10 const char vs_source[] =
11         "uniform float offset;\n"
12         "uniform vec2 lens_center;\n"
13         "uniform vec3 scale;\n"
14         "varying vec2 texcoord;\n"
15         "void main()\n"
16         "{\n"
17         "       gl_Position = vec4(gl_Vertex.x*0.5+offset, gl_Vertex.yzw);\n"
18         "       texcoord = (gl_Vertex.xy*0.5+0.5-lens_center)*scale.xy;\n"
19         "}\n";
20
21 const char fs_source[] =
22         "uniform sampler2D texture;\n"
23         "uniform vec4 distortion;\n"
24         "uniform vec4 chromatic;\n"
25         "uniform vec2 eye_center;\n"
26         "uniform vec3 scale;\n"
27         "varying vec2 texcoord;\n"
28         "void main()\n"
29         "{\n"
30         "       float r_sq = dot(texcoord, texcoord);\n"
31         "       float f = dot(distortion, vec4(1.0, r_sq, r_sq*r_sq, r_sq*r_sq*r_sq));\n"
32         "       vec2 dtc = (texcoord*f-eye_center)/(scale.xy*scale.z)+0.5;\n"
33         "       if(dtc.x<0.0 || dtc.y<0.0 || dtc.x>1.0 || dtc.y>1.0)\n"
34         "               gl_FragColor = vec4(0.0);\n"
35         "       else\n"
36         "       {\n"
37         "               vec2 red_dtc = (texcoord*f*(chromatic[0]+chromatic[1]*r_sq)-eye_center)/(scale.xy*scale.z)+0.5;\n"
38         "               vec2 blue_dtc = (texcoord*f*(chromatic[2]+chromatic[3]*r_sq)-eye_center)/(scale.xy*scale.z)+0.5;\n"
39         "               gl_FragColor = vec4(texture2D(texture, red_dtc).r, texture2D(texture, dtc).g, texture2D(texture, blue_dtc).b, 1.0);\n"
40         "       }\n"
41         "}\n";
42
43 }
44
45 namespace Msp {
46 namespace VR {
47
48 OculusRiftCombiner::OculusRiftCombiner():
49         mesh(GL::VERTEX2),
50         shprog(vs_source, fs_source),
51         // Default values copied from the SDK
52         view_distance(0.438f),
53         lens_separation(0.424f),
54         eye_separation(0.42735f),
55         fill_factor(0.95f)
56 {
57         width_div = 2;
58
59         left_shdata.uniform("texture", 0);
60         left_shdata.uniform("offset", -0.5f);
61         right_shdata.uniform("texture", 0);
62         right_shdata.uniform("offset", 0.5f);
63
64         chromatic[0] = 1.0f;
65         chromatic[1] = 0.0f;
66         chromatic[2] = 1.0f;
67         chromatic[3] = 0.0f;
68
69         // This will also call update_parameters
70         set_distortion(1.0f, 0.22f, 0.24f);
71
72         GL::MeshBuilder bld(mesh);
73         bld.begin(GL::TRIANGLE_STRIP);
74         bld.vertex(-1, 1);
75         bld.vertex(-1, -1);
76         bld.vertex(1, 1);
77         bld.vertex(1, -1);
78         bld.end();
79 }
80
81 void OculusRiftCombiner::set_view_distance(float d)
82 {
83         view_distance = d;
84         update_parameters();
85 }
86
87 void OculusRiftCombiner::set_lens_separation(float s)
88 {
89         lens_separation = s;
90         update_parameters();
91 }
92
93 void OculusRiftCombiner::set_eye_separation(float s)
94 {
95         eye_separation = s;
96         update_parameters();
97 }
98
99 void OculusRiftCombiner::set_distortion(float d0, float d1, float d2, float d3)
100 {
101         distortion[0] = d0;
102         distortion[1] = d1;
103         distortion[2] = d2;
104         distortion[3] = d3;
105
106         update_parameters();
107 }
108
109 void OculusRiftCombiner::set_red_aberration(float c0, float c1)
110 {
111         chromatic[0] = c0;
112         chromatic[1] = c1;
113
114         update_parameters();
115 }
116
117 void OculusRiftCombiner::set_blue_aberration(float c2, float c3)
118 {
119         chromatic[2] = c2;
120         chromatic[3] = c3;
121
122         update_parameters();
123 }
124
125 void OculusRiftCombiner::set_fill_factor(float f)
126 {
127         fill_factor = f;
128         update_parameters();
129 }
130
131 void OculusRiftCombiner::update_parameters()
132 {
133         left_shdata.uniform4("distortion", distortion);
134         left_shdata.uniform4("chromatic", chromatic);
135         right_shdata.uniform4("distortion", distortion);
136         right_shdata.uniform4("chromatic", chromatic);
137
138         // Set lens center positions, in output texture coordinates
139         left_shdata.uniform("lens_center", 1.0f-lens_separation, 0.5);
140         right_shdata.uniform("lens_center", lens_separation, 0.5);
141
142         /* Compute distance between eye and lens centers, in sampling texture
143         coordinates. */
144         float eye_offset = distort((eye_separation-lens_separation)*2);
145         left_shdata.uniform("eye_center", -eye_offset, 0.0f);
146         right_shdata.uniform("eye_center", eye_offset, 0.0f);
147
148         /* Determine the necessary scaling factor to avoid quality degradation in
149         the center of the screen. */
150         float horiz_oversize = distort((fill_factor-lens_separation)*2)/((fill_factor-lens_separation)*2)-eye_offset;
151         float vert_oversize = distort(1.25f*fill_factor)/(1.25f*fill_factor);
152         oversize = min(horiz_oversize, vert_oversize);
153
154         left_shdata.uniform("scale", 2.0f, 2.5f, oversize);
155         right_shdata.uniform("scale", 2.0f, 2.5f, oversize);
156
157         fov = Geometry::atan(oversize*0.625f/view_distance)*2.0f;
158 }
159
160 float OculusRiftCombiner::distort(float r) const
161 {
162         float r_sq = r*r;
163         return r*(distortion[0]+(distortion[1]+(distortion[2]+distortion[3]*r_sq)*r_sq)*r_sq);
164 }
165
166 float OculusRiftCombiner::undistort(float r) const
167 {
168         float x = r;
169         while(1)
170         {
171                 float y = distort(x);
172                 if(abs(r-y)<1e-5)
173                         return x;
174
175                 float x_sq = x*x;
176                 float d = distortion[0]+(3*distortion[1]+(5*distortion[2]+7*distortion[3]*x_sq)*x_sq)*x_sq;
177                 x -= (y-r)/d;
178         }
179 }
180
181 void OculusRiftCombiner::render(const GL::Texture2D &left, const GL::Texture2D &right) const
182 {
183         GL::Bind bind_shprog(shprog);
184
185         GL::Bind bind_tex(left);
186         left_shdata.apply();
187         mesh.draw();
188
189         right.bind();
190         right_shdata.apply();
191         mesh.draw();
192 }
193
194 } // namespace VR
195 } // namespace Msp