|  | // | 
|  | // Copyright 2017 The ANGLE Project Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  | // | 
|  | // This sample shows basic usage of the GL_OVR_multiview2 extension. | 
|  |  | 
|  | #include "SampleApplication.h" | 
|  |  | 
|  | #include "util/geometry_utils.h" | 
|  | #include "util/shader_utils.h" | 
|  |  | 
|  | #include <iostream> | 
|  |  | 
|  | namespace | 
|  | { | 
|  |  | 
|  | void FillTranslationMatrix(float xOffset, float yOffset, float zOffset, float *matrix) | 
|  | { | 
|  | matrix[0] = 1.0f; | 
|  | matrix[1] = 0.0f; | 
|  | matrix[2] = 0.0f; | 
|  | matrix[3] = xOffset; | 
|  |  | 
|  | matrix[4] = 0.0f; | 
|  | matrix[5] = 1.0f; | 
|  | matrix[6] = 0.0f; | 
|  | matrix[7] = yOffset; | 
|  |  | 
|  | matrix[8]  = 0.0f; | 
|  | matrix[9]  = 0.0f; | 
|  | matrix[10] = 1.0f; | 
|  | matrix[11] = zOffset; | 
|  |  | 
|  | matrix[12] = 0.0f; | 
|  | matrix[13] = 0.0f; | 
|  | matrix[14] = 0.0f; | 
|  | matrix[15] = 1.0f; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class MultiviewSample : public SampleApplication | 
|  | { | 
|  | public: | 
|  | MultiviewSample(int argc, char **argv) | 
|  | : SampleApplication("Multiview", argc, argv, 3, 0), | 
|  | mMultiviewProgram(0), | 
|  | mMultiviewPersperiveUniformLoc(-1), | 
|  | mMultiviewLeftEyeCameraUniformLoc(-1), | 
|  | mMultiviewRightEyeCameraUniformLoc(-1), | 
|  | mMultiviewTranslationUniformLoc(-1), | 
|  | mMultiviewFBO(0), | 
|  | mColorTexture(0), | 
|  | mDepthTexture(0), | 
|  | mQuadVAO(0), | 
|  | mQuadVBO(0), | 
|  | mCubeVAO(0), | 
|  | mCubePosVBO(0), | 
|  | mCubeNormalVBO(0), | 
|  | mCubeIBO(0), | 
|  | mCombineProgram(0) | 
|  | {} | 
|  |  | 
|  | bool initialize() override | 
|  | { | 
|  | // Check whether the GL_OVR_multiview(2) extension is supported. If not, abort | 
|  | // initialization. | 
|  | const char *allExtensions = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)); | 
|  | const std::string paddedExtensions = std::string(" ") + allExtensions + std::string(" "); | 
|  | if ((paddedExtensions.find(std::string(" GL_OVR_multiview2 ")) == std::string::npos) && | 
|  | (paddedExtensions.find(std::string(" GL_OVR_multiview ")) == std::string::npos)) | 
|  | { | 
|  | std::cout << "GL_OVR_multiview(2) is not available." << std::endl; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // A view covers horizontally half of the screen. | 
|  | int viewWidth  = getWindow()->getWidth() / 2; | 
|  | int viewHeight = getWindow()->getHeight(); | 
|  |  | 
|  | // Create color and depth texture arrays with two layers to which we render each view. | 
|  | glGenTextures(1, &mColorTexture); | 
|  | glBindTexture(GL_TEXTURE_2D_ARRAY, mColorTexture); | 
|  | glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, viewWidth, viewHeight, 2, 0, GL_RGBA, | 
|  | GL_UNSIGNED_BYTE, nullptr); | 
|  | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 
|  | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 
|  |  | 
|  | glGenTextures(1, &mDepthTexture); | 
|  | glBindTexture(GL_TEXTURE_2D_ARRAY, mDepthTexture); | 
|  | glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, viewWidth, viewHeight, 2, 0, | 
|  | GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); | 
|  |  | 
|  | // Generate multiview framebuffer for layered rendering. | 
|  | glGenFramebuffers(1, &mMultiviewFBO); | 
|  | glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO); | 
|  | glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mColorTexture, 0, 0, | 
|  | 2); | 
|  | glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mDepthTexture, 0, 0, | 
|  | 2); | 
|  | GLenum drawBuffer = GL_COLOR_ATTACHMENT0; | 
|  | glDrawBuffers(1, &drawBuffer); | 
|  |  | 
|  | // Check that the framebuffer is complete. Abort initialization otherwise. | 
|  | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Create multiview program and query the uniform locations. | 
|  | // The program has two code paths based on the gl_ViewID_OVR attribute which tells us which | 
|  | // view is currently being rendered to. Based on it we decide which eye's camera matrix to | 
|  | // use. | 
|  | constexpr char kMultiviewVS[] = | 
|  | "#version 300 es\n" | 
|  | "#extension GL_OVR_multiview2 : require\n" | 
|  | "layout(num_views = 2) in;\n" | 
|  | "layout(location=0) in vec3 posIn;\n" | 
|  | "layout(location=1) in vec3 normalIn;\n" | 
|  | "uniform mat4 uPerspective;\n" | 
|  | "uniform mat4 uCameraLeftEye;\n" | 
|  | "uniform mat4 uCameraRightEye;\n" | 
|  | "uniform mat4 uTranslation;\n" | 
|  | "out vec3 oNormal;\n" | 
|  | "void main()\n" | 
|  | "{\n" | 
|  | "   vec4 p = uTranslation * vec4(posIn,1.);\n" | 
|  | "   if (gl_ViewID_OVR == 0u) {\n" | 
|  | "       p = uCameraLeftEye * p;\n" | 
|  | "   } else {\n" | 
|  | "       p = uCameraRightEye * p;\n" | 
|  | "   }\n" | 
|  | "   oNormal = normalIn;\n" | 
|  | "   gl_Position = uPerspective * p;\n" | 
|  | "}\n"; | 
|  |  | 
|  | constexpr char kMultiviewFS[] = | 
|  | "#version 300 es\n" | 
|  | "#extension GL_OVR_multiview2 : require\n" | 
|  | "precision mediump float;\n" | 
|  | "out vec4 color;\n" | 
|  | "in vec3 oNormal;\n" | 
|  | "void main()\n" | 
|  | "{\n" | 
|  | "   vec3 col = 0.5 * oNormal + vec3(0.5);\n" | 
|  | "   color = vec4(col, 1.);\n" | 
|  | "}\n"; | 
|  |  | 
|  | mMultiviewProgram = CompileProgram(kMultiviewVS, kMultiviewFS); | 
|  | if (!mMultiviewProgram) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | mMultiviewPersperiveUniformLoc = glGetUniformLocation(mMultiviewProgram, "uPerspective"); | 
|  | mMultiviewLeftEyeCameraUniformLoc = | 
|  | glGetUniformLocation(mMultiviewProgram, "uCameraLeftEye"); | 
|  | mMultiviewRightEyeCameraUniformLoc = | 
|  | glGetUniformLocation(mMultiviewProgram, "uCameraRightEye"); | 
|  | mMultiviewTranslationUniformLoc = glGetUniformLocation(mMultiviewProgram, "uTranslation"); | 
|  |  | 
|  | // Create a normal program to combine both layers of the color array texture. | 
|  | constexpr char kCombineVS[] = | 
|  | "#version 300 es\n" | 
|  | "in vec2 vIn;\n" | 
|  | "out vec2 uv;\n" | 
|  | "void main()\n" | 
|  | "{\n" | 
|  | "   gl_Position = vec4(vIn, 0., 1.);\n" | 
|  | "   uv = vIn * .5 + vec2(.5);\n" | 
|  | "}\n"; | 
|  |  | 
|  | constexpr char kCombineFS[] = | 
|  | "#version 300 es\n" | 
|  | "precision mediump float;\n" | 
|  | "precision mediump sampler2DArray;\n" | 
|  | "uniform sampler2DArray uMultiviewTex;\n" | 
|  | "in vec2 uv;\n" | 
|  | "out vec4 color;\n" | 
|  | "void main()\n" | 
|  | "{\n" | 
|  | "   float scaledX = 2.0 * uv.x;\n" | 
|  | "   float layer = floor(scaledX);\n" | 
|  | "   vec2 adjustedUV = vec2(fract(scaledX), uv.y);\n" | 
|  | "   vec3 texColor = texture(uMultiviewTex, vec3(adjustedUV, layer)).rgb;\n" | 
|  | "   color = vec4(texColor, 1.);\n" | 
|  | "}\n"; | 
|  |  | 
|  | mCombineProgram = CompileProgram(kCombineVS, kCombineFS); | 
|  | if (!mCombineProgram) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Generate a quad which covers the whole screen. | 
|  | glGenVertexArrays(1, &mQuadVAO); | 
|  | glBindVertexArray(mQuadVAO); | 
|  |  | 
|  | glGenBuffers(1, &mQuadVBO); | 
|  | glBindBuffer(GL_ARRAY_BUFFER, mQuadVBO); | 
|  | const float kQuadPositionData[] = {1.f, -1.f, 1.f, 1.f, -1.f, -1.f, -1.f, 1.f}; | 
|  | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, kQuadPositionData, GL_STATIC_DRAW); | 
|  | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); | 
|  | glEnableVertexAttribArray(0); | 
|  | glBindVertexArray(0); | 
|  |  | 
|  | // Generate a cube. | 
|  | GenerateCubeGeometry(1.0f, &mCube); | 
|  | glGenVertexArrays(1, &mCubeVAO); | 
|  | glBindVertexArray(mCubeVAO); | 
|  |  | 
|  | glGenBuffers(1, &mCubePosVBO); | 
|  | glBindBuffer(GL_ARRAY_BUFFER, mCubePosVBO); | 
|  | glBufferData(GL_ARRAY_BUFFER, sizeof(angle::Vector3) * mCube.positions.size(), | 
|  | mCube.positions.data(), GL_STATIC_DRAW); | 
|  | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); | 
|  | glEnableVertexAttribArray(0); | 
|  |  | 
|  | glGenBuffers(1, &mCubeNormalVBO); | 
|  | glBindBuffer(GL_ARRAY_BUFFER, mCubeNormalVBO); | 
|  | glBufferData(GL_ARRAY_BUFFER, sizeof(angle::Vector3) * mCube.normals.size(), | 
|  | mCube.normals.data(), GL_STATIC_DRAW); | 
|  | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); | 
|  | glEnableVertexAttribArray(1); | 
|  |  | 
|  | glGenBuffers(1, &mCubeIBO); | 
|  | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mCubeIBO); | 
|  | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * mCube.indices.size(), | 
|  | mCube.indices.data(), GL_STATIC_DRAW); | 
|  |  | 
|  | glBindVertexArray(0); | 
|  |  | 
|  | glEnable(GL_DEPTH_TEST); | 
|  | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void destroy() override | 
|  | { | 
|  | glDeleteProgram(mMultiviewProgram); | 
|  | glDeleteFramebuffers(1, &mMultiviewFBO); | 
|  | glDeleteTextures(1, &mColorTexture); | 
|  | glDeleteTextures(1, &mDepthTexture); | 
|  | glDeleteVertexArrays(1, &mQuadVAO); | 
|  | glDeleteBuffers(1, &mQuadVBO); | 
|  | glDeleteVertexArrays(1, &mCubeVAO); | 
|  | glDeleteBuffers(1, &mQuadVBO); | 
|  | glDeleteBuffers(1, &mCubePosVBO); | 
|  | glDeleteBuffers(1, &mCubeNormalVBO); | 
|  | glDeleteBuffers(1, &mCubeIBO); | 
|  | glDeleteProgram(mCombineProgram); | 
|  | } | 
|  |  | 
|  | void draw() override | 
|  | { | 
|  | // Draw to multiview fbo. | 
|  | { | 
|  | // Generate the perspective projection matrix. | 
|  | const int viewWidth          = getWindow()->getWidth() / 2; | 
|  | const int viewHeight         = getWindow()->getHeight(); | 
|  | const float kFOV             = 90.f; | 
|  | const float kNear            = 1.0f; | 
|  | const float kFar             = 100.0f; | 
|  | const float kPlaneDifference = kFar - kNear; | 
|  | const float kXYScale         = 1.f / (tanf(kFOV / 2.0f)); | 
|  | const float kAspectRatio     = static_cast<float>(viewWidth) / viewHeight; | 
|  | float kPerspectiveProjectionMatrix[16]; | 
|  | kPerspectiveProjectionMatrix[0] = kXYScale / kAspectRatio; | 
|  | kPerspectiveProjectionMatrix[1] = .0f; | 
|  | kPerspectiveProjectionMatrix[2] = .0f; | 
|  | kPerspectiveProjectionMatrix[3] = .0f; | 
|  |  | 
|  | kPerspectiveProjectionMatrix[4] = .0f; | 
|  | kPerspectiveProjectionMatrix[5] = kXYScale; | 
|  | kPerspectiveProjectionMatrix[6] = .0f; | 
|  | kPerspectiveProjectionMatrix[7] = .0f; | 
|  |  | 
|  | kPerspectiveProjectionMatrix[8]  = .0f; | 
|  | kPerspectiveProjectionMatrix[9]  = .0; | 
|  | kPerspectiveProjectionMatrix[10] = -kFar / kPlaneDifference; | 
|  | kPerspectiveProjectionMatrix[11] = -1.f; | 
|  |  | 
|  | kPerspectiveProjectionMatrix[12] = .0f; | 
|  | kPerspectiveProjectionMatrix[13] = .0; | 
|  | kPerspectiveProjectionMatrix[14] = -kFar * kNear / kPlaneDifference; | 
|  | kPerspectiveProjectionMatrix[15] = .0; | 
|  |  | 
|  | // Generate the camera matrices for the left and right eye. | 
|  | const float kXOffset = 1.5f; | 
|  | const float kYOffset = 1.5f; | 
|  | const float kZOffset = 5.0f; | 
|  | float kLeftCameraMatrix[16]; | 
|  | FillTranslationMatrix(kXOffset, -kYOffset, -kZOffset, kLeftCameraMatrix); | 
|  | float kRightCameraMatrix[16]; | 
|  | FillTranslationMatrix(-kXOffset, -kYOffset, -kZOffset, kRightCameraMatrix); | 
|  |  | 
|  | // Bind and clear the multiview framebuffer. | 
|  | glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO); | 
|  | glClearColor(0, 0, 0, 1); | 
|  | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
|  |  | 
|  | // Set the viewport to be the size as one of the views. | 
|  | glViewport(0, 0, viewWidth, viewHeight); | 
|  |  | 
|  | // Bind multiview program and set matrices. | 
|  | glUseProgram(mMultiviewProgram); | 
|  | glUniformMatrix4fv(mMultiviewPersperiveUniformLoc, 1, GL_TRUE, | 
|  | kPerspectiveProjectionMatrix); | 
|  | glUniformMatrix4fv(mMultiviewLeftEyeCameraUniformLoc, 1, GL_TRUE, kLeftCameraMatrix); | 
|  | glUniformMatrix4fv(mMultiviewRightEyeCameraUniformLoc, 1, GL_TRUE, kRightCameraMatrix); | 
|  |  | 
|  | glBindVertexArray(mCubeVAO); | 
|  |  | 
|  | // Draw first cube. | 
|  | float kTranslationMatrix[16]; | 
|  | FillTranslationMatrix(0.0f, 0.0f, 0.0f, kTranslationMatrix); | 
|  | glUniformMatrix4fv(mMultiviewTranslationUniformLoc, 1, GL_TRUE, kTranslationMatrix); | 
|  | glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCube.indices.size()), | 
|  | GL_UNSIGNED_SHORT, nullptr); | 
|  |  | 
|  | // Draw second cube. | 
|  | FillTranslationMatrix(1.0f, 1.0f, -2.0f, kTranslationMatrix); | 
|  | glUniformMatrix4fv(mMultiviewTranslationUniformLoc, 1, GL_TRUE, kTranslationMatrix); | 
|  | glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCube.indices.size()), | 
|  | GL_UNSIGNED_SHORT, nullptr); | 
|  |  | 
|  | glBindVertexArray(0); | 
|  | } | 
|  |  | 
|  | // Combine both views. | 
|  | { | 
|  | // Bind the default framebuffer object and clear. | 
|  | glBindFramebuffer(GL_FRAMEBUFFER, 0); | 
|  |  | 
|  | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
|  |  | 
|  | // Set the viewport to cover the whole screen. | 
|  | glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); | 
|  |  | 
|  | glUseProgram(mCombineProgram); | 
|  |  | 
|  | // Bind the 2D array texture to be used as a sampler. | 
|  | glUniform1i(glGetUniformLocation(mCombineProgram, "uMultiviewTex"), 0); | 
|  | glBindTexture(GL_TEXTURE_2D_ARRAY, mMultiviewFBO); | 
|  | glActiveTexture(GL_TEXTURE0); | 
|  |  | 
|  | // Draw a quad which covers the whole screen. Layer and texture coordinates are | 
|  | // calculated in the vertex shader based on the UV coordinates of the quad. | 
|  | glBindVertexArray(mQuadVAO); | 
|  | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 
|  | glBindVertexArray(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | GLuint mMultiviewProgram; | 
|  | GLint mMultiviewPersperiveUniformLoc; | 
|  | GLint mMultiviewLeftEyeCameraUniformLoc; | 
|  | GLint mMultiviewRightEyeCameraUniformLoc; | 
|  | GLint mMultiviewTranslationUniformLoc; | 
|  |  | 
|  | GLuint mMultiviewFBO; | 
|  | GLuint mColorTexture; | 
|  | GLuint mDepthTexture; | 
|  |  | 
|  | GLuint mQuadVAO; | 
|  | GLuint mQuadVBO; | 
|  |  | 
|  | CubeGeometry mCube; | 
|  | GLuint mCubeVAO; | 
|  | GLuint mCubePosVBO; | 
|  | GLuint mCubeNormalVBO; | 
|  | GLuint mCubeIBO; | 
|  |  | 
|  | GLuint mCombineProgram; | 
|  | }; | 
|  |  | 
|  | int main(int argc, char **argv) | 
|  | { | 
|  | MultiviewSample app(argc, argv); | 
|  | return app.run(); | 
|  | } |