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