Android平台上简单的FramebufferObject示例。
FramebufferObject的概念就不说了,参考OpenGL ES 2.0 Programming Guide的第10章。
下面是render framebuffer到texture的例子。
代码的主要流程是:
创建framebuffer,绑定framebuffer,render framebuffer到texture,切换回system提供的framebuffer,利用之前产生的texture.
方便起见,两个render流程用的同样的shader.
下面是renderer的代码,Test7Renderer.java
1 package com.android.jayce.test; 2 3 import java.nio.ByteBuffer; 4 import java.nio.ByteOrder; 5 import java.nio.FloatBuffer; 6 import java.nio.IntBuffer; 7 8 import javax.microedition.khronos.egl.EGLConfig; 9 import javax.microedition.khronos.opengles.GL10; 10 11 import android.content.Context; 12 import android.opengl.GLES20; 13 import android.opengl.GLSurfaceView; 14 import android.opengl.Matrix; 15 import android.os.SystemClock; 16 17 import com.android.jayce.test.R; 18 19 /** 20 * This class implements our custom renderer. Note that the GL10 parameter passed in is unused for OpenGL ES 2.0 21 * renderers -- the static class GLES20 is used instead. 22 */ 23 public class Test7Renderer implements GLSurfaceView.Renderer 24 { 25 /** Used for debug logs. */ 26 private static final String TAG = "Test7Renderer"; 27 28 private final Context mActivityContext; 29 30 /** 31 * Store the model matrix. This matrix is used to move models from object space (where each model can be thought 32 * of being located at the center of the universe) to world space. 33 */ 34 private float[] mModelMatrix = new float[16]; 35 36 /** 37 * Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space; 38 * it positions things relative to our eye. 39 */ 40 private float[] mViewMatrix = new float[16]; 41 42 /** Store the projection matrix. This is used to project the scene onto a 2D viewport. */ 43 private float[] mProjectionMatrix = new float[16]; 44 45 /** Allocate storage for the final combined matrix. This will be passed into the shader program. */ 46 private float[] mMVPMatrix = new float[16]; 47 48 /** Store our model data in a float buffer. */ 49 private final FloatBuffer mCubePositions; 50 private final FloatBuffer mCubeColors; 51 private final FloatBuffer mCubeTextureCoordinates; 52 53 /** This will be used to pass in the transformation matrix. */ 54 private int mMVPMatrixHandle; 55 56 /** This will be used to pass in the modelview matrix. */ 57 private int mMVMatrixHandle; 58 59 /** This will be used to pass in the texture. */ 60 private int mTextureUniformHandle; 61 62 /** This will be used to pass in model position information. */ 63 private int mPositionHandle; 64 65 /** This will be used to pass in model color information. */ 66 private int mColorHandle; 67 68 /** This will be used to pass in model texture coordinate information. */ 69 private int mTextureCoordinateHandle; 70 71 /** How many bytes per float. */ 72 private final int mBytesPerFloat = 4; 73 74 /** Size of the position data in elements. */ 75 private final int mPositionDataSize = 3; 76 77 /** Size of the color data in elements. */ 78 private final int mColorDataSize = 4; 79 80 /** Size of the texture coordinate data in elements. */ 81 private final int mTextureCoordinateDataSize = 2; 82 83 /** This is a handle to our cube shading program. */ 84 private int mProgramHandle; 85 86 /** This is a handle to our texture data. */ 87 private int mTextureDataHandle; 88 89 /** 90 * Initialize the model data. 91 */ 92 public Test7Renderer(final Context activityContext) 93 { 94 mActivityContext = activityContext; 95 96 // Define points for a cube. 97 98 // X, Y, Z 99 final float[] cubePositionData = 100 { 101 // In OpenGL counter-clockwise winding is default. This means that when we look at a triangle, 102 // if the points are counter-clockwise we are looking at the "front". If not we are looking at 103 // the back. OpenGL has an optimization where all back-facing triangles are culled, since they 104 // usually represent the backside of an object and aren't visible anyways. 105 106 // Front face 107 -1.0f, 1.0f, 1.0f, 108 -1.0f, -1.0f, 1.0f, 109 1.0f, 1.0f, 1.0f, 110 -1.0f, -1.0f, 1.0f, 111 1.0f, -1.0f, 1.0f, 112 1.0f, 1.0f, 1.0f, 113 114 // Right face 115 1.0f, 1.0f, 1.0f, 116 1.0f, -1.0f, 1.0f, 117 1.0f, 1.0f, -1.0f, 118 1.0f, -1.0f, 1.0f, 119 1.0f, -1.0f, -1.0f, 120 1.0f, 1.0f, -1.0f, 121 122 // Back face 123 1.0f, 1.0f, -1.0f, 124 1.0f, -1.0f, -1.0f, 125 -1.0f, 1.0f, -1.0f, 126 1.0f, -1.0f, -1.0f, 127 -1.0f, -1.0f, -1.0f, 128 -1.0f, 1.0f, -1.0f, 129 130 // Left face 131 -1.0f, 1.0f, -1.0f, 132 -1.0f, -1.0f, -1.0f, 133 -1.0f, 1.0f, 1.0f, 134 -1.0f, -1.0f, -1.0f, 135 -1.0f, -1.0f, 1.0f, 136 -1.0f, 1.0f, 1.0f, 137 138 // Top face 139 -1.0f, 1.0f, -1.0f, 140 -1.0f, 1.0f, 1.0f, 141 1.0f, 1.0f, -1.0f, 142 -1.0f, 1.0f, 1.0f, 143 1.0f, 1.0f, 1.0f, 144 1.0f, 1.0f, -1.0f, 145 146 // Bottom face 147 1.0f, -1.0f, -1.0f, 148 1.0f, -1.0f, 1.0f, 149 -1.0f, -1.0f, -1.0f, 150 1.0f, -1.0f, 1.0f, 151 -1.0f, -1.0f, 1.0f, 152 -1.0f, -1.0f, -1.0f, 153 }; 154 155 // R, G, B, A 156 final float[] cubeColorData = 157 { 158 // Front face (red) 159 1.0f, 0.0f, 0.0f, 1.0f, 160 1.0f, 0.0f, 0.0f, 1.0f, 161 1.0f, 0.0f, 0.0f, 1.0f, 162 1.0f, 0.0f, 0.0f, 1.0f, 163 1.0f, 0.0f, 0.0f, 1.0f, 164 1.0f, 0.0f, 0.0f, 1.0f, 165 166 // Right face (green) 167 0.0f, 1.0f, 0.0f, 1.0f, 168 0.0f, 1.0f, 0.0f, 1.0f, 169 0.0f, 1.0f, 0.0f, 1.0f, 170 0.0f, 1.0f, 0.0f, 1.0f, 171 0.0f, 1.0f, 0.0f, 1.0f, 172 0.0f, 1.0f, 0.0f, 1.0f, 173 174 // Back face (blue) 175 0.0f, 0.0f, 1.0f, 1.0f, 176 0.0f, 0.0f, 1.0f, 1.0f, 177 0.0f, 0.0f, 1.0f, 1.0f, 178 0.0f, 0.0f, 1.0f, 1.0f, 179 0.0f, 0.0f, 1.0f, 1.0f, 180 0.0f, 0.0f, 1.0f, 1.0f, 181 182 // Left face (yellow) 183 1.0f, 1.0f, 0.0f, 1.0f, 184 1.0f, 1.0f, 0.0f, 1.0f, 185 1.0f, 1.0f, 0.0f, 1.0f, 186 1.0f, 1.0f, 0.0f, 1.0f, 187 1.0f, 1.0f, 0.0f, 1.0f, 188 1.0f, 1.0f, 0.0f, 1.0f, 189 190 // Top face (cyan) 191 0.0f, 1.0f, 1.0f, 1.0f, 192 0.0f, 1.0f, 1.0f, 1.0f, 193 0.0f, 1.0f, 1.0f, 1.0f, 194 0.0f, 1.0f, 1.0f, 1.0f, 195 0.0f, 1.0f, 1.0f, 1.0f, 196 0.0f, 1.0f, 1.0f, 1.0f, 197 198 // Bottom face (magenta) 199 1.0f, 0.0f, 1.0f, 1.0f, 200 1.0f, 0.0f, 1.0f, 1.0f, 201 1.0f, 0.0f, 1.0f, 1.0f, 202 1.0f, 0.0f, 1.0f, 1.0f, 203 1.0f, 0.0f, 1.0f, 1.0f, 204 1.0f, 0.0f, 1.0f, 1.0f 205 }; 206 207 // S, T (or X, Y) 208 // Texture coordinate data. 209 // Because images have a Y axis pointing downward (values increase as you move down the image) while 210 // OpenGL has a Y axis pointing upward, we adjust for that here by flipping the Y axis. 211 // What's more is that the texture coordinates are the same for every face. 212 final float[] cubeTextureCoordinateData = 213 { 214 // Front face 215 0.0f, 0.0f, 216 0.0f, 1.0f, 217 1.0f, 0.0f, 218 0.0f, 1.0f, 219 1.0f, 1.0f, 220 1.0f, 0.0f, 221 222 // Right face 223 0.0f, 0.0f, 224 0.0f, 1.0f, 225 1.0f, 0.0f, 226 0.0f, 1.0f, 227 1.0f, 1.0f, 228 1.0f, 0.0f, 229 230 // Back face 231 0.0f, 0.0f, 232 0.0f, 1.0f, 233 1.0f, 0.0f, 234 0.0f, 1.0f, 235 1.0f, 1.0f, 236 1.0f, 0.0f, 237 238 // Left face 239 0.0f, 0.0f, 240 0.0f, 1.0f, 241 1.0f, 0.0f, 242 0.0f, 1.0f, 243 1.0f, 1.0f, 244 1.0f, 0.0f, 245 246 // Top face 247 0.0f, 0.0f, 248 0.0f, 1.0f, 249 1.0f, 0.0f, 250 0.0f, 1.0f, 251 1.0f, 1.0f, 252 1.0f, 0.0f, 253 254 // Bottom face 255 0.0f, 0.0f, 256 0.0f, 1.0f, 257 1.0f, 0.0f, 258 0.0f, 1.0f, 259 1.0f, 1.0f, 260 1.0f, 0.0f 261 }; 262 263 // Initialize the buffers. 264 mCubePositions = ByteBuffer.allocateDirect(cubePositionData.length * mBytesPerFloat) 265 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 266 mCubePositions.put(cubePositionData).position(0); 267 268 mCubeColors = ByteBuffer.allocateDirect(cubeColorData.length * mBytesPerFloat) 269 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 270 mCubeColors.put(cubeColorData).position(0); 271 272 mCubeTextureCoordinates = ByteBuffer.allocateDirect(cubeTextureCoordinateData.length * mBytesPerFloat) 273 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 274 mCubeTextureCoordinates.put(cubeTextureCoordinateData).position(0); 275 } 276 277 protected String getVertexShader(int shader) 278 { 279 return ToolsUtil.readTextFileFromRawResource(mActivityContext, shader); 280 } 281 282 protected String getFragmentShader(int shader) 283 { 284 return ToolsUtil.readTextFileFromRawResource(mActivityContext, shader); 285 } 286 287 @Override 288 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 289 { 290 // Set the background clear color to black. 291 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 292 293 // Use culling to remove back faces. 294 GLES20.glEnable(GLES20.GL_CULL_FACE); 295 296 // Enable depth testing 297 GLES20.glEnable(GLES20.GL_DEPTH_TEST); 298 299 // The below glEnable() call is a holdover from OpenGL ES 1, and is not needed in OpenGL ES 2. 300 // Enable texture mapping 301 GLES20.glEnable(GLES20.GL_TEXTURE_2D); 302 303 // Position the eye in front of the origin. 304 final float eyeX = 0.0f; 305 final float eyeY = 0.0f; 306 final float eyeZ = -0.5f; 307 308 // We are looking toward the distance 309 final float lookX = 0.0f; 310 final float lookY = 0.0f; 311 final float lookZ = -5.0f; 312 313 // Set our up vector. This is where our head would be pointing were we holding the camera. 314 final float upX = 0.0f; 315 final float upY = 1.0f; 316 final float upZ = 0.0f; 317 318 // Set the view matrix. This matrix can be said to represent the camera position. 319 // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and 320 // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose. 321 Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); 322 323 final String vertexShader = getVertexShader(R.raw.per_pixel_vertex_shader); 324 final String fragmentShader = getFragmentShader(R.raw.per_pixel_fragment_shader); 325 326 final int vertexShaderHandle = ToolsUtil.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader); 327 final int fragmentShaderHandle = ToolsUtil.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader); 328 329 mProgramHandle = ToolsUtil.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, 330 new String[] {"a_Position", "a_Color", "a_TexCoordinate"}); 331 332 // Load the texture 333 mTextureDataHandle = ToolsUtil.loadTexture(mActivityContext, R.drawable.aaa); 334 } 335 336 @Override 337 public void onSurfaceChanged(GL10 glUnused, int width, int height) 338 { 339 // Set the OpenGL viewport to the same size as the surface. 340 GLES20.glViewport(0, 0, width, height); 341 342 // Create a new perspective projection matrix. The height will stay the same 343 // while the width will vary as per aspect ratio. 344 final float ratio = (float) width / height; 345 final float left = -ratio; 346 final float right = ratio; 347 final float bottom = -1.0f; 348 final float top = 1.0f; 349 final float near = 1.0f; 350 final float far = 10.0f; 351 352 Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far); 353 } 354 355 @Override 356 public void onDrawFrame(GL10 glUnused) 357 { 358 IntBuffer framebuffer = IntBuffer.allocate(1); 359 IntBuffer depthRenderbuffer = IntBuffer.allocate(1); 360 IntBuffer texture = IntBuffer.allocate(1); 361 int texWidth = 480, texHeight = 480; 362 IntBuffer maxRenderbufferSize = IntBuffer.allocate(1); 363 GLES20.glGetIntegerv(GLES20.GL_MAX_RENDERBUFFER_SIZE, maxRenderbufferSize); 364 // check if GL_MAX_RENDERBUFFER_SIZE is >= texWidth and texHeight 365 if((maxRenderbufferSize.get(0) <= texWidth) || 366 (maxRenderbufferSize.get(0) <= texHeight)) 367 { 368 // cannot use framebuffer objects as we need to create 369 // a depth buffer as a renderbuffer object 370 // return with appropriate error 371 } 372 // generate the framebuffer, renderbuffer, and texture object names 373 GLES20.glGenFramebuffers(1, framebuffer); 374 GLES20.glGenRenderbuffers(1, depthRenderbuffer); 375 GLES20.glGenTextures(1, texture); 376 // bind texture and load the texture mip-level 0 377 // texels are RGB565 378 // no texels need to be specified as we are going to draw into 379 // the texture 380 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture.get(0)); 381 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, texWidth, texHeight, 382 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, null); 383 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 384 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); 385 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); 386 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); 387 // bind renderbuffer and create a 16-bit depth buffer 388 // width and height of renderbuffer = width and height of 389 // the texture 390 GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthRenderbuffer.get(0)); 391 GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, 392 texWidth, texHeight); 393 // bind the framebuffer 394 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer.get(0)); 395 // specify texture as color attachment 396 GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, 397 GLES20.GL_TEXTURE_2D, texture.get(0), 0); 398 // specify depth_renderbufer as depth attachment 399 GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, 400 GLES20.GL_RENDERBUFFER, depthRenderbuffer.get(0)); 401 // check for framebuffer complete 402 int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); 403 if(status == GLES20.GL_FRAMEBUFFER_COMPLETE) 404 { 405 // render to texture using FBO 406 GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 407 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 408 409 // Do a complete rotation every 10 seconds. 410 long time = SystemClock.uptimeMillis() % 10000L; 411 float angleInDegrees = (360.0f / 10000.0f) * (2 * (int) time); 412 413 GLES20.glUseProgram(mProgramHandle); 414 415 // Set program handles for cube drawing. 416 mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix"); 417 mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix"); 418 mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture"); 419 mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); 420 mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color"); 421 mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); 422 423 // Set the active texture unit to texture unit 0. 424 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 425 426 // Bind the texture to this unit. 427 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); 428 429 // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0. 430 GLES20.glUniform1i(mTextureUniformHandle, 0); 431 432 Matrix.setIdentityM(mModelMatrix, 0); 433 Matrix.translateM(mModelMatrix, 0, 0.0f, -1.0f, -5.0f); 434 Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f); 435 drawCube(); 436 437 // render to window system provided framebuffer 438 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 439 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 440 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 441 442 // Do a complete rotation every 10 seconds. 443 time = SystemClock.uptimeMillis() % 10000L; 444 angleInDegrees = (360.0f / 10000.0f) * ((int) time); 445 446 GLES20.glUseProgram(mProgramHandle); 447 448 // Set program handles for cube drawing. 449 mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix"); 450 mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix"); 451 mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture"); 452 mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); 453 mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color"); 454 mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); 455 456 // Set the active texture unit to texture unit 0. 457 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 458 459 // Bind the texture to this unit. 460 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture.get(0)/*mTextureDataHandle*/); 461 462 // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0. 463 GLES20.glUniform1i(mTextureUniformHandle, 0); 464 465 Matrix.setIdentityM(mModelMatrix, 0); 466 Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -5.0f); 467 Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f); 468 drawCube(); 469 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); 470 } 471 472 // cleanup 473 GLES20.glDeleteRenderbuffers(1, depthRenderbuffer); 474 GLES20.glDeleteFramebuffers(1, framebuffer); 475 GLES20.glDeleteTextures(1, texture); 476 } 477 478 /** 479 * Draws a cube. 480 */ 481 private void drawCube() 482 { 483 // Pass in the position information 484 mCubePositions.position(0); 485 GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, 486 0, mCubePositions); 487 488 GLES20.glEnableVertexAttribArray(mPositionHandle); 489 490 // Pass in the color information 491 mCubeColors.position(0); 492 GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, 493 0, mCubeColors); 494 GLES20.glEnableVertexAttribArray(mColorHandle); 495 496 // Pass in the texture coordinate information 497 mCubeTextureCoordinates.position(0); 498 GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 499 0, mCubeTextureCoordinates); 500 501 GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); 502 503 // This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix 504 // (which currently contains model * view). 505 Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); 506 507 // Pass in the modelview matrix. 508 GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0); 509 510 // This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix 511 // (which now contains model * view * projection). 512 Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); 513 514 // Pass in the combined matrix. 515 GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0); 516 517 // Draw the cube. 518 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); 519 } 520 }
还有一个辅助类,ToolsUtil.java:
1 package com.android.jayce.test; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.opengl.GLES20; 7 import android.opengl.GLUtils; 8 import android.util.Log; 9 10 import java.io.BufferedReader; 11 import java.io.IOException; 12 import java.io.InputStream; 13 import java.io.InputStreamReader; 14 15 public class ToolsUtil 16 { 17 public static int loadTexture(final Context context, final int resourceId) 18 { 19 final int[] textureHandle = new int[1]; 20 GLES20.glGenTextures(1, textureHandle, 0); 21 22 if(textureHandle[0] != 0) 23 { 24 final BitmapFactory.Options options = new BitmapFactory.Options(); 25 options.inScaled = false; 26 27 final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options); 28 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]); 29 30 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); 31 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); 32 33 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 34 bitmap.recycle(); 35 } 36 37 if(textureHandle[0] == 0) 38 { 39 throw new RuntimeException("failed to load texture"); 40 } 41 42 return textureHandle[0]; 43 } 44 45 /** 46 * Helper function to compile a shader. 47 * 48 * @param shaderType The shader type. 49 * @param shaderSource The shader source code. 50 * @return An OpenGL handle to the shader. 51 */ 52 public static int compileShader(final int shaderType, final String shaderSource) 53 { 54 int shaderHandle = GLES20.glCreateShader(shaderType); 55 56 if (shaderHandle != 0) 57 { 58 // Pass in the shader source. 59 GLES20.glShaderSource(shaderHandle, shaderSource); 60 61 // Compile the shader. 62 GLES20.glCompileShader(shaderHandle); 63 64 // Get the compilation status. 65 final int[] compileStatus = new int[1]; 66 GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0); 67 68 // If the compilation failed, delete the shader. 69 if (compileStatus[0] == 0) 70 { 71 GLES20.glDeleteShader(shaderHandle); 72 shaderHandle = 0; 73 } 74 } 75 76 if (shaderHandle == 0) 77 { 78 throw new RuntimeException("Error creating shader."); 79 } 80 81 return shaderHandle; 82 } 83 84 /** 85 * Helper function to compile and link a program. 86 * 87 * @param vertexShaderHandle An OpenGL handle to an already-compiled vertex shader. 88 * @param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader. 89 * @param attributes Attributes that need to be bound to the program. 90 * @return An OpenGL handle to the program. 91 */ 92 public static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) 93 { 94 int programHandle = GLES20.glCreateProgram(); 95 96 if (programHandle != 0) 97 { 98 // Bind the vertex shader to the program. 99 GLES20.glAttachShader(programHandle, vertexShaderHandle); 100 101 // Bind the fragment shader to the program. 102 GLES20.glAttachShader(programHandle, fragmentShaderHandle); 103 104 // Bind attributes 105 if (attributes != null) 106 { 107 final int size = attributes.length; 108 for (int i = 0; i < size; i++) 109 { 110 GLES20.glBindAttribLocation(programHandle, i, attributes[i]); 111 } 112 } 113 114 // Link the two shaders together into a program. 115 GLES20.glLinkProgram(programHandle); 116 117 // Get the link status. 118 final int[] linkStatus = new int[1]; 119 GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0); 120 121 // If the link failed, delete the program. 122 if (linkStatus[0] == 0) 123 { 124 GLES20.glDeleteProgram(programHandle); 125 programHandle = 0; 126 } 127 } 128 129 if (programHandle == 0) 130 { 131 throw new RuntimeException("Error creating program."); 132 } 133 134 return programHandle; 135 } 136 137 public static String readTextFileFromRawResource(final Context context, 138 final int resourceId) 139 { 140 final InputStream inputStream = context.getResources().openRawResource( 141 resourceId); 142 final InputStreamReader inputStreamReader = new InputStreamReader( 143 inputStream); 144 final BufferedReader bufferedReader = new BufferedReader( 145 inputStreamReader); 146 147 String nextLine; 148 final StringBuilder body = new StringBuilder(); 149 150 try 151 { 152 while ((nextLine = bufferedReader.readLine()) != null) 153 { 154 body.append(nextLine); 155 body.append('\n'); 156 } 157 } 158 catch (IOException e) 159 { 160 return null; 161 } 162 163 return body.toString(); 164 } 165 166 }
使用的shader, per_pixel_vertex_shader.glsl:
1 uniform mat4 u_MVPMatrix; // A constant representing the combined model/view/projection matrix. 2 uniform mat4 u_MVMatrix; // A constant representing the combined model/view matrix. 3 4 attribute vec4 a_Position; // Per-vertex position information we will pass in. 5 attribute vec4 a_Color; // Per-vertex color information we will pass in. 6 attribute vec2 a_TexCoordinate; // Per-vertex texture coordinate information we will pass in. 7 8 varying vec3 v_Position; // This will be passed into the fragment shader. 9 varying vec4 v_Color; // This will be passed into the fragment shader. 10 varying vec2 v_TexCoordinate; // This will be passed into the fragment shader. 11 12 // The entry point for our vertex shader. 13 void main() 14 { 15 // Transform the vertex into eye space. 16 v_Position = vec3(u_MVMatrix * a_Position); 17 18 // Pass through the color. 19 v_Color = a_Color; 20 21 // Pass through the texture coordinate. 22 v_TexCoordinate = a_TexCoordinate; 23 24 // gl_Position is a special variable used to store the final position. 25 // Multiply the vertex by the matrix to get the final point in normalized screen coordinates. 26 gl_Position = u_MVPMatrix * a_Position; 27 }
使用的shader, per_pixel_fragment_shader.glsl:
1 precision mediump float; // Set the default precision to medium. We don't need as high of a 2 // precision in the fragment shader. 3 uniform sampler2D u_Texture; // The input texture. 4 5 varying vec3 v_Position; // Interpolated position for this fragment. 6 varying vec4 v_Color; // This is the color from the vertex shader interpolated across the 7 varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment. 8 9 // The entry point for our fragment shader. 10 void main() 11 { 12 // Multiply the color by the diffuse illumination level and texture value to get final output color. 13 gl_FragColor = (v_Color * texture2D(u_Texture, v_TexCoordinate)); 14 }
好了,就这么多了,可以看到旋转立方体每一面的texture都是用的自己创建的framebuffer render的texture,每面都有一个旋转的立方体。
看看效果图吧: