1 /* 2 * Copyright 2018 Google LLC All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.skar.examples.helloskar.rendering; 18 19 import android.content.Context; 20 import android.opengl.GLES11Ext; 21 import android.opengl.GLES20; 22 import android.opengl.GLSurfaceView; 23 24 import com.google.ar.core.Frame; 25 import com.google.ar.core.Session; 26 27 import java.io.IOException; 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 import java.nio.FloatBuffer; 31 32 /** 33 * This class renders the AR background from camera feed. It creates and hosts the texture given to 34 * ARCore to be filled with the camera image. 35 */ 36 public class BackgroundRenderer { 37 private static final String TAG = BackgroundRenderer.class.getSimpleName(); 38 39 // Shader names. 40 private static final String VERTEX_SHADER_NAME = "shaders/screenquad.vert"; 41 private static final String FRAGMENT_SHADER_NAME = "shaders/screenquad.frag"; 42 43 private static final int COORDS_PER_VERTEX = 3; 44 private static final int TEXCOORDS_PER_VERTEX = 2; 45 private static final int FLOAT_SIZE = 4; 46 47 private FloatBuffer quadVertices; 48 private FloatBuffer quadTexCoord; 49 private FloatBuffer quadTexCoordTransformed; 50 51 private int quadProgram; 52 53 private int quadPositionParam; 54 private int quadTexCoordParam; 55 private int textureId = -1; 56 BackgroundRenderer()57 public BackgroundRenderer() { 58 } 59 getTextureId()60 public int getTextureId() { 61 return textureId; 62 } 63 64 /** 65 * Allocates and initializes OpenGL resources needed by the background renderer. Must be called on 66 * the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, 67 * EGLConfig)}. 68 * 69 * @param context Needed to access shader source. 70 */ createOnGlThread(Context context)71 public void createOnGlThread(Context context) throws IOException { 72 // Generate the background texture. 73 int[] textures = new int[1]; 74 GLES20.glGenTextures(1, textures, 0); 75 textureId = textures[0]; 76 int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; 77 GLES20.glBindTexture(textureTarget, textureId); 78 GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 79 GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); 80 GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); 81 GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); 82 83 int numVertices = 4; 84 if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) { 85 throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer."); 86 } 87 88 ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE); 89 bbVertices.order(ByteOrder.nativeOrder()); 90 quadVertices = bbVertices.asFloatBuffer(); 91 quadVertices.put(QUAD_COORDS); 92 quadVertices.position(0); 93 94 ByteBuffer bbTexCoords = 95 ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); 96 bbTexCoords.order(ByteOrder.nativeOrder()); 97 quadTexCoord = bbTexCoords.asFloatBuffer(); 98 quadTexCoord.put(QUAD_TEXCOORDS); 99 quadTexCoord.position(0); 100 101 ByteBuffer bbTexCoordsTransformed = 102 ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); 103 bbTexCoordsTransformed.order(ByteOrder.nativeOrder()); 104 quadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer(); 105 106 int vertexShader = 107 ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME); 108 int fragmentShader = 109 ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME); 110 111 quadProgram = GLES20.glCreateProgram(); 112 GLES20.glAttachShader(quadProgram, vertexShader); 113 GLES20.glAttachShader(quadProgram, fragmentShader); 114 GLES20.glLinkProgram(quadProgram); 115 GLES20.glUseProgram(quadProgram); 116 117 ShaderUtil.checkGLError(TAG, "Program creation"); 118 119 quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position"); 120 quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord"); 121 122 ShaderUtil.checkGLError(TAG, "Program parameters"); 123 } 124 125 /** 126 * Draws the AR background image. The image will be drawn such that virtual content rendered with 127 * the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and 128 * {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will 129 * accurately follow static physical objects. This must be called <b>before</b> drawing virtual 130 * content. 131 * 132 * @param frame The last {@code Frame} returned by {@link Session#update()}. 133 */ draw(Frame frame)134 public void draw(Frame frame) { 135 // If display rotation changed (also includes view size change), we need to re-query the uv 136 // coordinates for the screen rect, as they may have changed as well. 137 if (frame.hasDisplayGeometryChanged()) { 138 frame.transformDisplayUvCoords(quadTexCoord, quadTexCoordTransformed); 139 } 140 141 // No need to test or write depth, the screen quad has arbitrary depth, and is expected 142 // to be drawn first. 143 GLES20.glDisable(GLES20.GL_DEPTH_TEST); 144 GLES20.glDepthMask(false); 145 146 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); 147 148 GLES20.glUseProgram(quadProgram); 149 150 // Set the vertex positions. 151 GLES20.glVertexAttribPointer( 152 quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadVertices); 153 154 // Set the texture coordinates. 155 GLES20.glVertexAttribPointer( 156 quadTexCoordParam, 157 TEXCOORDS_PER_VERTEX, 158 GLES20.GL_FLOAT, 159 false, 160 0, 161 quadTexCoordTransformed); 162 163 // Enable vertex arrays 164 GLES20.glEnableVertexAttribArray(quadPositionParam); 165 GLES20.glEnableVertexAttribArray(quadTexCoordParam); 166 167 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 168 169 // Disable vertex arrays 170 GLES20.glDisableVertexAttribArray(quadPositionParam); 171 GLES20.glDisableVertexAttribArray(quadTexCoordParam); 172 173 // Restore the depth state for further drawing. 174 GLES20.glDepthMask(true); 175 GLES20.glEnable(GLES20.GL_DEPTH_TEST); 176 177 ShaderUtil.checkGLError(TAG, "Draw"); 178 } 179 180 private static final float[] QUAD_COORDS = 181 new float[]{ 182 -1.0f, -1.0f, 0.0f, -1.0f, +1.0f, 0.0f, +1.0f, -1.0f, 0.0f, +1.0f, +1.0f, 0.0f, 183 }; 184 185 private static final float[] QUAD_TEXCOORDS = 186 new float[]{ 187 0.0f, 1.0f, 188 0.0f, 0.0f, 189 1.0f, 1.0f, 190 1.0f, 0.0f, 191 }; 192 } 193