Home | History | Annotate | Download | only in graphics
      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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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          */
    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 
    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
    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
    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
    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