| // |
| // 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(); |
| } |