1 /* 2 * Copyright (C) 2007 The Android Open Source Project 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.example.android.apis.graphics; 18 19 import javax.microedition.khronos.egl.EGLConfig; 20 import javax.microedition.khronos.opengles.GL; 21 import javax.microedition.khronos.opengles.GL10; 22 import javax.microedition.khronos.opengles.GL11ExtensionPack; 23 24 import android.app.Activity; 25 import android.opengl.GLSurfaceView; 26 import android.opengl.GLU; 27 import android.os.Bundle; 28 import android.os.SystemClock; 29 30 /** 31 * Demonstrate the Frame Buffer Object OpenGL ES extension. 32 * <p> 33 * This sample renders a scene into an offscreen frame buffer, and then 34 * uses the resulting image as a texture to render an onscreen scene. 35 */ 36 public class FrameBufferObjectActivity extends Activity { 37 private GLSurfaceView mGLSurfaceView; 38 39 private class Renderer implements GLSurfaceView.Renderer { 40 private boolean mContextSupportsFrameBufferObject; 41 private int mTargetTexture; 42 private int mFramebuffer; 43 private int mFramebufferWidth = 256; 44 private int mFramebufferHeight = 256; 45 private int mSurfaceWidth; 46 private int mSurfaceHeight; 47 48 private Triangle mTriangle; 49 private Cube mCube; 50 private float mAngle; 51 /** 52 * Setting this to true will change the behavior of this sample. It 53 * will suppress the normally onscreen rendering, and it will cause the 54 * rendering that would normally be done to the offscreen FBO 55 * be rendered onscreen instead. This can be helpful in debugging the 56 * rendering algorithm. 57 */ 58 private static final boolean DEBUG_RENDER_OFFSCREEN_ONSCREEN = false; 59 onDrawFrame(GL10 gl)60 public void onDrawFrame(GL10 gl) { 61 checkGLError(gl); 62 if (mContextSupportsFrameBufferObject) { 63 GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl; 64 if (DEBUG_RENDER_OFFSCREEN_ONSCREEN) { 65 drawOffscreenImage(gl, mSurfaceWidth, mSurfaceHeight); 66 } else { 67 gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFramebuffer); 68 drawOffscreenImage(gl, mFramebufferWidth, mFramebufferHeight); 69 gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); 70 drawOnscreen(gl, mSurfaceWidth, mSurfaceHeight); 71 } 72 } else { 73 // Current context doesn't support frame buffer objects. 74 // Indicate this by drawing a red background. 75 gl.glClearColor(1,0,0,0); 76 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 77 } 78 } 79 onSurfaceChanged(GL10 gl, int width, int height)80 public void onSurfaceChanged(GL10 gl, int width, int height) { 81 checkGLError(gl); 82 mSurfaceWidth = width; 83 mSurfaceHeight = height; 84 gl.glViewport(0, 0, width, height); 85 } 86 onSurfaceCreated(GL10 gl, EGLConfig config)87 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 88 mContextSupportsFrameBufferObject = checkIfContextSupportsFrameBufferObject(gl); 89 if (mContextSupportsFrameBufferObject) { 90 mTargetTexture = createTargetTexture(gl, mFramebufferWidth, mFramebufferHeight); 91 mFramebuffer = createFrameBuffer(gl, mFramebufferWidth, mFramebufferHeight, mTargetTexture); 92 93 mCube = new Cube(); 94 mTriangle = new Triangle(); 95 } 96 } 97 drawOnscreen(GL10 gl, int width, int height)98 private void drawOnscreen(GL10 gl, int width, int height) { 99 gl.glViewport(0, 0, width, height); 100 float ratio = (float) width / height; 101 gl.glMatrixMode(GL10.GL_PROJECTION); 102 gl.glLoadIdentity(); 103 gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); 104 105 gl.glClearColor(0,0,1,0); 106 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 107 gl.glBindTexture(GL10.GL_TEXTURE_2D, mTargetTexture); 108 109 gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, 110 GL10.GL_REPLACE); 111 112 gl.glMatrixMode(GL10.GL_MODELVIEW); 113 gl.glLoadIdentity(); 114 115 GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 116 117 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 118 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 119 120 gl.glActiveTexture(GL10.GL_TEXTURE0); 121 122 long time = SystemClock.uptimeMillis() % 4000L; 123 float angle = 0.090f * ((int) time); 124 125 gl.glRotatef(angle, 0, 0, 1.0f); 126 127 mTriangle.draw(gl); 128 129 // Restore default state so the other renderer is not affected. 130 131 gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); 132 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 133 gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 134 } 135 drawOffscreenImage(GL10 gl, int width, int height)136 private void drawOffscreenImage(GL10 gl, int width, int height) { 137 gl.glViewport(0, 0, width, height); 138 float ratio = (float) width / height; 139 gl.glMatrixMode(GL10.GL_PROJECTION); 140 gl.glLoadIdentity(); 141 gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); 142 143 gl.glEnable(GL10.GL_CULL_FACE); 144 gl.glEnable(GL10.GL_DEPTH_TEST); 145 146 gl.glClearColor(0,0.5f,1,0); 147 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 148 gl.glMatrixMode(GL10.GL_MODELVIEW); 149 gl.glLoadIdentity(); 150 gl.glTranslatef(0, 0, -3.0f); 151 gl.glRotatef(mAngle, 0, 1, 0); 152 gl.glRotatef(mAngle*0.25f, 1, 0, 0); 153 154 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 155 gl.glEnableClientState(GL10.GL_COLOR_ARRAY); 156 157 mCube.draw(gl); 158 159 gl.glRotatef(mAngle*2.0f, 0, 1, 1); 160 gl.glTranslatef(0.5f, 0.5f, 0.5f); 161 162 mCube.draw(gl); 163 164 mAngle += 1.2f; 165 166 // Restore default state so the other renderer is not affected. 167 168 gl.glDisable(GL10.GL_CULL_FACE); 169 gl.glDisable(GL10.GL_DEPTH_TEST); 170 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 171 gl.glDisableClientState(GL10.GL_COLOR_ARRAY); 172 } 173 createTargetTexture(GL10 gl, int width, int height)174 private int createTargetTexture(GL10 gl, int width, int height) { 175 int texture; 176 int[] textures = new int[1]; 177 gl.glGenTextures(1, textures, 0); 178 texture = textures[0]; 179 gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); 180 gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, width, height, 0, 181 GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null); 182 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, 183 GL10.GL_NEAREST); 184 gl.glTexParameterf(GL10.GL_TEXTURE_2D, 185 GL10.GL_TEXTURE_MAG_FILTER, 186 GL10.GL_LINEAR); 187 gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, 188 GL10.GL_REPEAT); 189 gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, 190 GL10.GL_REPEAT); 191 ; return texture; 192 } 193 createFrameBuffer(GL10 gl, int width, int height, int targetTextureId)194 private int createFrameBuffer(GL10 gl, int width, int height, int targetTextureId) { 195 GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl; 196 int framebuffer; 197 int[] framebuffers = new int[1]; 198 gl11ep.glGenFramebuffersOES(1, framebuffers, 0); 199 framebuffer = framebuffers[0]; 200 gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, framebuffer); 201 202 int depthbuffer; 203 int[] renderbuffers = new int[1]; 204 gl11ep.glGenRenderbuffersOES(1, renderbuffers, 0); 205 depthbuffer = renderbuffers[0]; 206 207 gl11ep.glBindRenderbufferOES(GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer); 208 gl11ep.glRenderbufferStorageOES(GL11ExtensionPack.GL_RENDERBUFFER_OES, 209 GL11ExtensionPack.GL_DEPTH_COMPONENT16, width, height); 210 gl11ep.glFramebufferRenderbufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 211 GL11ExtensionPack.GL_DEPTH_ATTACHMENT_OES, 212 GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer); 213 214 gl11ep.glFramebufferTexture2DOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 215 GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, GL10.GL_TEXTURE_2D, 216 targetTextureId, 0); 217 int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES); 218 if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) { 219 throw new RuntimeException("Framebuffer is not complete: " + 220 Integer.toHexString(status)); 221 } 222 gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); 223 return framebuffer; 224 } 225 checkIfContextSupportsFrameBufferObject(GL10 gl)226 private boolean checkIfContextSupportsFrameBufferObject(GL10 gl) { 227 return checkIfContextSupportsExtension(gl, "GL_OES_framebuffer_object"); 228 } 229 230 /** 231 * This is not the fastest way to check for an extension, but fine if 232 * we are only checking for a few extensions each time a context is created. 233 * @param gl 234 * @param extension 235 * @return true if the extension is present in the current context. 236 */ checkIfContextSupportsExtension(GL10 gl, String extension)237 private boolean checkIfContextSupportsExtension(GL10 gl, String extension) { 238 String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " "; 239 // The extensions string is padded with spaces between extensions, but not 240 // necessarily at the beginning or end. For simplicity, add spaces at the 241 // beginning and end of the extensions string and the extension string. 242 // This means we can avoid special-case checks for the first or last 243 // extension, as well as avoid special-case checks when an extension name 244 // is the same as the first part of another extension name. 245 return extensions.indexOf(" " + extension + " ") >= 0; 246 } 247 } 248 checkGLError(GL gl)249 static void checkGLError(GL gl) { 250 int error = ((GL10) gl).glGetError(); 251 if (error != GL10.GL_NO_ERROR) { 252 throw new RuntimeException("GLError 0x" + Integer.toHexString(error)); 253 } 254 } 255 256 @Override onCreate(Bundle savedInstanceState)257 protected void onCreate(Bundle savedInstanceState) { 258 super.onCreate(savedInstanceState); 259 260 // Create our surface view and set it as the content of our 261 // Activity 262 mGLSurfaceView = new GLSurfaceView(this); 263 mGLSurfaceView.setRenderer(new Renderer()); 264 setContentView(mGLSurfaceView); 265 } 266 267 @Override onResume()268 protected void onResume() { 269 // Ideally a game should implement onResume() and onPause() 270 // to take appropriate action when the activity looses focus 271 super.onResume(); 272 mGLSurfaceView.onResume(); 273 } 274 275 @Override onPause()276 protected void onPause() { 277 // Ideally a game should implement onResume() and onPause() 278 // to take appropriate action when the activity looses focus 279 super.onPause(); 280 mGLSurfaceView.onPause(); 281 } 282 } 283