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