blob: acf3dc52d9807cf3822fdb915915d7367b0c2d25 [file] [log] [blame]
/*
* 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,
};
}