1 //
2 // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // This sample shows basic usage of the GL_OVR_multiview2 extension.
7
8 #include "SampleApplication.h"
9
10 #include "util/geometry_utils.h"
11 #include "util/shader_utils.h"
12
13 #include <iostream>
14
15 namespace
16 {
17
FillTranslationMatrix(float xOffset,float yOffset,float zOffset,float * matrix)18 void FillTranslationMatrix(float xOffset, float yOffset, float zOffset, float *matrix)
19 {
20 matrix[0] = 1.0f;
21 matrix[1] = 0.0f;
22 matrix[2] = 0.0f;
23 matrix[3] = xOffset;
24
25 matrix[4] = 0.0f;
26 matrix[5] = 1.0f;
27 matrix[6] = 0.0f;
28 matrix[7] = yOffset;
29
30 matrix[8] = 0.0f;
31 matrix[9] = 0.0f;
32 matrix[10] = 1.0f;
33 matrix[11] = zOffset;
34
35 matrix[12] = 0.0f;
36 matrix[13] = 0.0f;
37 matrix[14] = 0.0f;
38 matrix[15] = 1.0f;
39 }
40
41 } // namespace
42
43 class MultiviewSample : public SampleApplication
44 {
45 public:
MultiviewSample(int argc,char ** argv)46 MultiviewSample(int argc, char **argv)
47 : SampleApplication("Multiview", argc, argv, 3, 0),
48 mMultiviewProgram(0),
49 mMultiviewPersperiveUniformLoc(-1),
50 mMultiviewLeftEyeCameraUniformLoc(-1),
51 mMultiviewRightEyeCameraUniformLoc(-1),
52 mMultiviewTranslationUniformLoc(-1),
53 mMultiviewFBO(0),
54 mColorTexture(0),
55 mDepthTexture(0),
56 mQuadVAO(0),
57 mQuadVBO(0),
58 mCubeVAO(0),
59 mCubePosVBO(0),
60 mCubeNormalVBO(0),
61 mCubeIBO(0),
62 mCombineProgram(0)
63 {}
64
initialize()65 bool initialize() override
66 {
67 // Check whether the GL_OVR_multiview(2) extension is supported. If not, abort
68 // initialization.
69 const char *allExtensions = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
70 const std::string paddedExtensions = std::string(" ") + allExtensions + std::string(" ");
71 if ((paddedExtensions.find(std::string(" GL_OVR_multiview2 ")) == std::string::npos) &&
72 (paddedExtensions.find(std::string(" GL_OVR_multiview ")) == std::string::npos))
73 {
74 std::cout << "GL_OVR_multiview(2) is not available." << std::endl;
75 return false;
76 }
77
78 // A view covers horizontally half of the screen.
79 int viewWidth = getWindow()->getWidth() / 2;
80 int viewHeight = getWindow()->getHeight();
81
82 // Create color and depth texture arrays with two layers to which we render each view.
83 glGenTextures(1, &mColorTexture);
84 glBindTexture(GL_TEXTURE_2D_ARRAY, mColorTexture);
85 glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, viewWidth, viewHeight, 2, 0, GL_RGBA,
86 GL_UNSIGNED_BYTE, nullptr);
87 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
88 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
89
90 glGenTextures(1, &mDepthTexture);
91 glBindTexture(GL_TEXTURE_2D_ARRAY, mDepthTexture);
92 glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, viewWidth, viewHeight, 2, 0,
93 GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
94
95 // Generate multiview framebuffer for layered rendering.
96 glGenFramebuffers(1, &mMultiviewFBO);
97 glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
98 glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mColorTexture, 0, 0,
99 2);
100 glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mDepthTexture, 0, 0,
101 2);
102 GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
103 glDrawBuffers(1, &drawBuffer);
104
105 // Check that the framebuffer is complete. Abort initialization otherwise.
106 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
107 {
108 return false;
109 }
110
111 // Create multiview program and query the uniform locations.
112 // The program has two code paths based on the gl_ViewID_OVR attribute which tells us which
113 // view is currently being rendered to. Based on it we decide which eye's camera matrix to
114 // use.
115 constexpr char kMultiviewVS[] =
116 "#version 300 es\n"
117 "#extension GL_OVR_multiview2 : require\n"
118 "layout(num_views = 2) in;\n"
119 "layout(location=0) in vec3 posIn;\n"
120 "layout(location=1) in vec3 normalIn;\n"
121 "uniform mat4 uPerspective;\n"
122 "uniform mat4 uCameraLeftEye;\n"
123 "uniform mat4 uCameraRightEye;\n"
124 "uniform mat4 uTranslation;\n"
125 "out vec3 oNormal;\n"
126 "void main()\n"
127 "{\n"
128 " vec4 p = uTranslation * vec4(posIn,1.);\n"
129 " if (gl_ViewID_OVR == 0u) {\n"
130 " p = uCameraLeftEye * p;\n"
131 " } else {\n"
132 " p = uCameraRightEye * p;\n"
133 " }\n"
134 " oNormal = normalIn;\n"
135 " gl_Position = uPerspective * p;\n"
136 "}\n";
137
138 constexpr char kMultiviewFS[] =
139 "#version 300 es\n"
140 "#extension GL_OVR_multiview2 : require\n"
141 "precision mediump float;\n"
142 "out vec4 color;\n"
143 "in vec3 oNormal;\n"
144 "void main()\n"
145 "{\n"
146 " vec3 col = 0.5 * oNormal + vec3(0.5);\n"
147 " color = vec4(col, 1.);\n"
148 "}\n";
149
150 mMultiviewProgram = CompileProgram(kMultiviewVS, kMultiviewFS);
151 if (!mMultiviewProgram)
152 {
153 return false;
154 }
155 mMultiviewPersperiveUniformLoc = glGetUniformLocation(mMultiviewProgram, "uPerspective");
156 mMultiviewLeftEyeCameraUniformLoc =
157 glGetUniformLocation(mMultiviewProgram, "uCameraLeftEye");
158 mMultiviewRightEyeCameraUniformLoc =
159 glGetUniformLocation(mMultiviewProgram, "uCameraRightEye");
160 mMultiviewTranslationUniformLoc = glGetUniformLocation(mMultiviewProgram, "uTranslation");
161
162 // Create a normal program to combine both layers of the color array texture.
163 constexpr char kCombineVS[] =
164 "#version 300 es\n"
165 "in vec2 vIn;\n"
166 "out vec2 uv;\n"
167 "void main()\n"
168 "{\n"
169 " gl_Position = vec4(vIn, 0., 1.);\n"
170 " uv = vIn * .5 + vec2(.5);\n"
171 "}\n";
172
173 constexpr char kCombineFS[] =
174 "#version 300 es\n"
175 "precision mediump float;\n"
176 "precision mediump sampler2DArray;\n"
177 "uniform sampler2DArray uMultiviewTex;\n"
178 "in vec2 uv;\n"
179 "out vec4 color;\n"
180 "void main()\n"
181 "{\n"
182 " float scaledX = 2.0 * uv.x;\n"
183 " float layer = floor(scaledX);\n"
184 " vec2 adjustedUV = vec2(fract(scaledX), uv.y);\n"
185 " vec3 texColor = texture(uMultiviewTex, vec3(adjustedUV, layer)).rgb;\n"
186 " color = vec4(texColor, 1.);\n"
187 "}\n";
188
189 mCombineProgram = CompileProgram(kCombineVS, kCombineFS);
190 if (!mCombineProgram)
191 {
192 return false;
193 }
194
195 // Generate a quad which covers the whole screen.
196 glGenVertexArrays(1, &mQuadVAO);
197 glBindVertexArray(mQuadVAO);
198
199 glGenBuffers(1, &mQuadVBO);
200 glBindBuffer(GL_ARRAY_BUFFER, mQuadVBO);
201 const float kQuadPositionData[] = {1.f, -1.f, 1.f, 1.f, -1.f, -1.f, -1.f, 1.f};
202 glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, kQuadPositionData, GL_STATIC_DRAW);
203 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
204 glEnableVertexAttribArray(0);
205 glBindVertexArray(0);
206
207 // Generate a cube.
208 GenerateCubeGeometry(1.0f, &mCube);
209 glGenVertexArrays(1, &mCubeVAO);
210 glBindVertexArray(mCubeVAO);
211
212 glGenBuffers(1, &mCubePosVBO);
213 glBindBuffer(GL_ARRAY_BUFFER, mCubePosVBO);
214 glBufferData(GL_ARRAY_BUFFER, sizeof(angle::Vector3) * mCube.positions.size(),
215 mCube.positions.data(), GL_STATIC_DRAW);
216 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
217 glEnableVertexAttribArray(0);
218
219 glGenBuffers(1, &mCubeNormalVBO);
220 glBindBuffer(GL_ARRAY_BUFFER, mCubeNormalVBO);
221 glBufferData(GL_ARRAY_BUFFER, sizeof(angle::Vector3) * mCube.normals.size(),
222 mCube.normals.data(), GL_STATIC_DRAW);
223 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
224 glEnableVertexAttribArray(1);
225
226 glGenBuffers(1, &mCubeIBO);
227 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mCubeIBO);
228 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * mCube.indices.size(),
229 mCube.indices.data(), GL_STATIC_DRAW);
230
231 glBindVertexArray(0);
232
233 glEnable(GL_DEPTH_TEST);
234 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
235
236 return true;
237 }
238
destroy()239 void destroy() override
240 {
241 glDeleteProgram(mMultiviewProgram);
242 glDeleteFramebuffers(1, &mMultiviewFBO);
243 glDeleteTextures(1, &mColorTexture);
244 glDeleteTextures(1, &mDepthTexture);
245 glDeleteVertexArrays(1, &mQuadVAO);
246 glDeleteBuffers(1, &mQuadVBO);
247 glDeleteVertexArrays(1, &mCubeVAO);
248 glDeleteBuffers(1, &mQuadVBO);
249 glDeleteBuffers(1, &mCubePosVBO);
250 glDeleteBuffers(1, &mCubeNormalVBO);
251 glDeleteBuffers(1, &mCubeIBO);
252 glDeleteProgram(mCombineProgram);
253 }
254
draw()255 void draw() override
256 {
257 // Draw to multiview fbo.
258 {
259 // Generate the perspective projection matrix.
260 const int viewWidth = getWindow()->getWidth() / 2;
261 const int viewHeight = getWindow()->getHeight();
262 const float kFOV = 90.f;
263 const float kNear = 1.0f;
264 const float kFar = 100.0f;
265 const float kPlaneDifference = kFar - kNear;
266 const float kXYScale = 1.f / (tanf(kFOV / 2.0f));
267 const float kAspectRatio = static_cast<float>(viewWidth) / viewHeight;
268 float kPerspectiveProjectionMatrix[16];
269 kPerspectiveProjectionMatrix[0] = kXYScale / kAspectRatio;
270 kPerspectiveProjectionMatrix[1] = .0f;
271 kPerspectiveProjectionMatrix[2] = .0f;
272 kPerspectiveProjectionMatrix[3] = .0f;
273
274 kPerspectiveProjectionMatrix[4] = .0f;
275 kPerspectiveProjectionMatrix[5] = kXYScale;
276 kPerspectiveProjectionMatrix[6] = .0f;
277 kPerspectiveProjectionMatrix[7] = .0f;
278
279 kPerspectiveProjectionMatrix[8] = .0f;
280 kPerspectiveProjectionMatrix[9] = .0;
281 kPerspectiveProjectionMatrix[10] = -kFar / kPlaneDifference;
282 kPerspectiveProjectionMatrix[11] = -1.f;
283
284 kPerspectiveProjectionMatrix[12] = .0f;
285 kPerspectiveProjectionMatrix[13] = .0;
286 kPerspectiveProjectionMatrix[14] = -kFar * kNear / kPlaneDifference;
287 kPerspectiveProjectionMatrix[15] = .0;
288
289 // Generate the camera matrices for the left and right eye.
290 const float kXOffset = 1.5f;
291 const float kYOffset = 1.5f;
292 const float kZOffset = 5.0f;
293 float kLeftCameraMatrix[16];
294 FillTranslationMatrix(kXOffset, -kYOffset, -kZOffset, kLeftCameraMatrix);
295 float kRightCameraMatrix[16];
296 FillTranslationMatrix(-kXOffset, -kYOffset, -kZOffset, kRightCameraMatrix);
297
298 // Bind and clear the multiview framebuffer.
299 glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
300 glClearColor(0, 0, 0, 1);
301 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
302
303 // Set the viewport to be the size as one of the views.
304 glViewport(0, 0, viewWidth, viewHeight);
305
306 // Bind multiview program and set matrices.
307 glUseProgram(mMultiviewProgram);
308 glUniformMatrix4fv(mMultiviewPersperiveUniformLoc, 1, GL_TRUE,
309 kPerspectiveProjectionMatrix);
310 glUniformMatrix4fv(mMultiviewLeftEyeCameraUniformLoc, 1, GL_TRUE, kLeftCameraMatrix);
311 glUniformMatrix4fv(mMultiviewRightEyeCameraUniformLoc, 1, GL_TRUE, kRightCameraMatrix);
312
313 glBindVertexArray(mCubeVAO);
314
315 // Draw first cube.
316 float kTranslationMatrix[16];
317 FillTranslationMatrix(0.0f, 0.0f, 0.0f, kTranslationMatrix);
318 glUniformMatrix4fv(mMultiviewTranslationUniformLoc, 1, GL_TRUE, kTranslationMatrix);
319 glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCube.indices.size()),
320 GL_UNSIGNED_SHORT, nullptr);
321
322 // Draw second cube.
323 FillTranslationMatrix(1.0f, 1.0f, -2.0f, kTranslationMatrix);
324 glUniformMatrix4fv(mMultiviewTranslationUniformLoc, 1, GL_TRUE, kTranslationMatrix);
325 glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCube.indices.size()),
326 GL_UNSIGNED_SHORT, nullptr);
327
328 glBindVertexArray(0);
329 }
330
331 // Combine both views.
332 {
333 // Bind the default framebuffer object and clear.
334 glBindFramebuffer(GL_FRAMEBUFFER, 0);
335
336 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
337
338 // Set the viewport to cover the whole screen.
339 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
340
341 glUseProgram(mCombineProgram);
342
343 // Bind the 2D array texture to be used as a sampler.
344 glUniform1i(glGetUniformLocation(mCombineProgram, "uMultiviewTex"), 0);
345 glBindTexture(GL_TEXTURE_2D_ARRAY, mMultiviewFBO);
346 glActiveTexture(GL_TEXTURE0);
347
348 // Draw a quad which covers the whole screen. Layer and texture coordinates are
349 // calculated in the vertex shader based on the UV coordinates of the quad.
350 glBindVertexArray(mQuadVAO);
351 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
352 glBindVertexArray(0);
353 }
354 }
355
356 private:
357 GLuint mMultiviewProgram;
358 GLint mMultiviewPersperiveUniformLoc;
359 GLint mMultiviewLeftEyeCameraUniformLoc;
360 GLint mMultiviewRightEyeCameraUniformLoc;
361 GLint mMultiviewTranslationUniformLoc;
362
363 GLuint mMultiviewFBO;
364 GLuint mColorTexture;
365 GLuint mDepthTexture;
366
367 GLuint mQuadVAO;
368 GLuint mQuadVBO;
369
370 CubeGeometry mCube;
371 GLuint mCubeVAO;
372 GLuint mCubePosVBO;
373 GLuint mCubeNormalVBO;
374 GLuint mCubeIBO;
375
376 GLuint mCombineProgram;
377 };
378
main(int argc,char ** argv)379 int main(int argc, char **argv)
380 {
381 MultiviewSample app(argc, argv);
382 return app.run();
383 }
384