| /* |
| * Copyright 2018 Google LLC All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.skar.examples.helloskar.rendering; |
| |
| import android.content.Context; |
| import android.opengl.GLES11Ext; |
| import android.opengl.GLES20; |
| import android.opengl.GLSurfaceView; |
| |
| import com.google.ar.core.Frame; |
| import com.google.ar.core.Session; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.FloatBuffer; |
| |
| /** |
| * This class renders the AR background from camera feed. It creates and hosts the texture given to |
| * ARCore to be filled with the camera image. |
| */ |
| public class BackgroundRenderer { |
| private static final String TAG = BackgroundRenderer.class.getSimpleName(); |
| |
| // Shader names. |
| private static final String VERTEX_SHADER_NAME = "shaders/screenquad.vert"; |
| private static final String FRAGMENT_SHADER_NAME = "shaders/screenquad.frag"; |
| |
| private static final int COORDS_PER_VERTEX = 3; |
| private static final int TEXCOORDS_PER_VERTEX = 2; |
| private static final int FLOAT_SIZE = 4; |
| |
| private FloatBuffer quadVertices; |
| private FloatBuffer quadTexCoord; |
| private FloatBuffer quadTexCoordTransformed; |
| |
| private int quadProgram; |
| |
| private int quadPositionParam; |
| private int quadTexCoordParam; |
| private int textureId = -1; |
| |
| public BackgroundRenderer() { |
| } |
| |
| public int getTextureId() { |
| return textureId; |
| } |
| |
| /** |
| * Allocates and initializes OpenGL resources needed by the background renderer. Must be called on |
| * the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, |
| * EGLConfig)}. |
| * |
| * @param context Needed to access shader source. |
| */ |
| public void createOnGlThread(Context context) throws IOException { |
| // Generate the background texture. |
| int[] textures = new int[1]; |
| GLES20.glGenTextures(1, textures, 0); |
| textureId = textures[0]; |
| int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; |
| GLES20.glBindTexture(textureTarget, textureId); |
| GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); |
| GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); |
| GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); |
| GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); |
| |
| int numVertices = 4; |
| if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) { |
| throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer."); |
| } |
| |
| ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE); |
| bbVertices.order(ByteOrder.nativeOrder()); |
| quadVertices = bbVertices.asFloatBuffer(); |
| quadVertices.put(QUAD_COORDS); |
| quadVertices.position(0); |
| |
| ByteBuffer bbTexCoords = |
| ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); |
| bbTexCoords.order(ByteOrder.nativeOrder()); |
| quadTexCoord = bbTexCoords.asFloatBuffer(); |
| quadTexCoord.put(QUAD_TEXCOORDS); |
| quadTexCoord.position(0); |
| |
| ByteBuffer bbTexCoordsTransformed = |
| ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); |
| bbTexCoordsTransformed.order(ByteOrder.nativeOrder()); |
| quadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer(); |
| |
| int vertexShader = |
| ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME); |
| int fragmentShader = |
| ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME); |
| |
| quadProgram = GLES20.glCreateProgram(); |
| GLES20.glAttachShader(quadProgram, vertexShader); |
| GLES20.glAttachShader(quadProgram, fragmentShader); |
| GLES20.glLinkProgram(quadProgram); |
| GLES20.glUseProgram(quadProgram); |
| |
| ShaderUtil.checkGLError(TAG, "Program creation"); |
| |
| quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position"); |
| quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord"); |
| |
| ShaderUtil.checkGLError(TAG, "Program parameters"); |
| } |
| |
| /** |
| * Draws the AR background image. The image will be drawn such that virtual content rendered with |
| * the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and |
| * {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will |
| * accurately follow static physical objects. This must be called <b>before</b> drawing virtual |
| * content. |
| * |
| * @param frame The last {@code Frame} returned by {@link Session#update()}. |
| */ |
| public void draw(Frame frame) { |
| // If display rotation changed (also includes view size change), we need to re-query the uv |
| // coordinates for the screen rect, as they may have changed as well. |
| if (frame.hasDisplayGeometryChanged()) { |
| frame.transformDisplayUvCoords(quadTexCoord, quadTexCoordTransformed); |
| } |
| |
| // No need to test or write depth, the screen quad has arbitrary depth, and is expected |
| // to be drawn first. |
| GLES20.glDisable(GLES20.GL_DEPTH_TEST); |
| GLES20.glDepthMask(false); |
| |
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); |
| |
| GLES20.glUseProgram(quadProgram); |
| |
| // Set the vertex positions. |
| GLES20.glVertexAttribPointer( |
| quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadVertices); |
| |
| // Set the texture coordinates. |
| GLES20.glVertexAttribPointer( |
| quadTexCoordParam, |
| TEXCOORDS_PER_VERTEX, |
| GLES20.GL_FLOAT, |
| false, |
| 0, |
| quadTexCoordTransformed); |
| |
| // Enable vertex arrays |
| GLES20.glEnableVertexAttribArray(quadPositionParam); |
| GLES20.glEnableVertexAttribArray(quadTexCoordParam); |
| |
| GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); |
| |
| // Disable vertex arrays |
| GLES20.glDisableVertexAttribArray(quadPositionParam); |
| GLES20.glDisableVertexAttribArray(quadTexCoordParam); |
| |
| // Restore the depth state for further drawing. |
| GLES20.glDepthMask(true); |
| GLES20.glEnable(GLES20.GL_DEPTH_TEST); |
| |
| ShaderUtil.checkGLError(TAG, "Draw"); |
| } |
| |
| private static final float[] QUAD_COORDS = |
| new float[]{ |
| -1.0f, -1.0f, 0.0f, -1.0f, +1.0f, 0.0f, +1.0f, -1.0f, 0.0f, +1.0f, +1.0f, 0.0f, |
| }; |
| |
| private static final float[] QUAD_TEXCOORDS = |
| new float[]{ |
| 0.0f, 1.0f, |
| 0.0f, 0.0f, |
| 1.0f, 1.0f, |
| 1.0f, 0.0f, |
| }; |
| } |