还是一个旋转的立方体,现在给它加上材质。载入一个图片,立方体的每一面都贴上此图片。
现在相较第二篇画的立方体,又多了个参数材质。先看看载入图片的方法。
这是个辅助方法。单独写了个类。
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 9 public class ToolsUtil 10 { 11 public static int loadTexture(final Context context, final int resourceId) 12 { 13 final int[] textureHandle = new int[1]; 14 GLES20.glGenTextures(1, textureHandle, 0); 15 16 if(textureHandle[0] != 0) 17 { 18 final BitmapFactory.Options options = new BitmapFactory.Options(); 19 options.inScaled = false; 20 21 final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options); 22 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]); 23 24 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); 25 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); 26 27 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 28 bitmap.recycle(); 29 } 30 31 if(textureHandle[0] == 0) 32 { 33 throw new RuntimeException("failed to load texture"); 34 } 35 36 return textureHandle[0]; 37 } 38 }
在Renderer里多了材质坐标,mCubeTextureCoordinates。
在fragmentShader里,颜色计算变成了gl_FragColor = v_Color * texture2D(u_Texture, v_TexCoordinate)。
看Renderer的实现:
Test5Renderer.java
1 package com.android.jayce.test; 2 3 import java.nio.ByteBuffer; 4 import java.nio.ByteOrder; 5 import java.nio.FloatBuffer; 6 7 import javax.microedition.khronos.egl.EGLConfig; 8 import javax.microedition.khronos.opengles.GL10; 9 10 import com.learnopengles.android.R; 11 12 import android.content.Context; 13 import android.opengl.GLES20; 14 import android.opengl.Matrix; 15 import android.opengl.GLSurfaceView; 16 import android.os.SystemClock; 17 import android.util.Log; 18 19 public class Test5Renderer implements GLSurfaceView.Renderer 20 { 21 private static final String TAG = "Test5Renderer"; 22 private Context mContext; 23 private static final int BYTES_PER_FLOAT = 4; 24 private final FloatBuffer mCubePositions; 25 private final FloatBuffer mCubeColors; 26 private final FloatBuffer mCubeTextureCoordinates; 27 private float[] mMVPMatrix = new float[16]; 28 private float[] mViewMatrix = new float[16]; 29 private float[] mModelMatrix = new float[16]; 30 private float[] mProjectionMatrix = new float[16]; 31 private int mMVPMatrixHandle; 32 private int mPositionHandle; 33 private int mColorHandle; 34 private int mTextureUniformHandle; 35 private int mTextureCoordinateHandle; 36 private int mTextureDataHandle; 37 private int mProgramHandle; 38 private final int POSITION_DATA_SIZE = 3; 39 private final int COLOR_DATA_SIZE = 4; 40 private final int TEXTURE_COORDINATE_DATA_SIZE = 2; 41 42 public Test5Renderer(final Context context) 43 { 44 mContext = context; 45 final float cubePosition[] = 46 { 47 // Front face 48 -1.0f, 1.0f, 1.0f, 49 -1.0f, -1.0f, 1.0f, 50 1.0f, 1.0f, 1.0f, 51 -1.0f, -1.0f, 1.0f, 52 1.0f, -1.0f, 1.0f, 53 1.0f, 1.0f, 1.0f, 54 55 // Right face 56 1.0f, 1.0f, 1.0f, 57 1.0f, -1.0f, 1.0f, 58 1.0f, 1.0f, -1.0f, 59 1.0f, -1.0f, 1.0f, 60 1.0f, -1.0f, -1.0f, 61 1.0f, 1.0f, -1.0f, 62 63 // Back face 64 1.0f, 1.0f, -1.0f, 65 1.0f, -1.0f, -1.0f, 66 -1.0f, 1.0f, -1.0f, 67 1.0f, -1.0f, -1.0f, 68 -1.0f, -1.0f, -1.0f, 69 -1.0f, 1.0f, -1.0f, 70 71 // Left face 72 -1.0f, 1.0f, -1.0f, 73 -1.0f, -1.0f, -1.0f, 74 -1.0f, 1.0f, 1.0f, 75 -1.0f, -1.0f, -1.0f, 76 -1.0f, -1.0f, 1.0f, 77 -1.0f, 1.0f, 1.0f, 78 79 // Top face 80 -1.0f, 1.0f, -1.0f, 81 -1.0f, 1.0f, 1.0f, 82 1.0f, 1.0f, -1.0f, 83 -1.0f, 1.0f, 1.0f, 84 1.0f, 1.0f, 1.0f, 85 1.0f, 1.0f, -1.0f, 86 87 // Bottom face 88 1.0f, -1.0f, -1.0f, 89 1.0f, -1.0f, 1.0f, 90 -1.0f, -1.0f, -1.0f, 91 1.0f, -1.0f, 1.0f, 92 -1.0f, -1.0f, 1.0f, 93 -1.0f, -1.0f, -1.0f, 94 }; 95 96 final float[] cubeColor = 97 { 98 // Front face (red) 99 1.0f, 0.0f, 0.0f, 1.0f, 100 1.0f, 0.0f, 0.0f, 1.0f, 101 1.0f, 0.0f, 0.0f, 1.0f, 102 1.0f, 0.0f, 0.0f, 1.0f, 103 1.0f, 0.0f, 0.0f, 1.0f, 104 1.0f, 0.0f, 0.0f, 1.0f, 105 106 // Right face (green) 107 0.0f, 1.0f, 0.0f, 1.0f, 108 0.0f, 1.0f, 0.0f, 1.0f, 109 0.0f, 1.0f, 0.0f, 1.0f, 110 0.0f, 1.0f, 0.0f, 1.0f, 111 0.0f, 1.0f, 0.0f, 1.0f, 112 0.0f, 1.0f, 0.0f, 1.0f, 113 114 // Back face (blue) 115 0.0f, 0.0f, 1.0f, 1.0f, 116 0.0f, 0.0f, 1.0f, 1.0f, 117 0.0f, 0.0f, 1.0f, 1.0f, 118 0.0f, 0.0f, 1.0f, 1.0f, 119 0.0f, 0.0f, 1.0f, 1.0f, 120 0.0f, 0.0f, 1.0f, 1.0f, 121 122 // Left face (yellow) 123 1.0f, 1.0f, 0.0f, 1.0f, 124 1.0f, 1.0f, 0.0f, 1.0f, 125 1.0f, 1.0f, 0.0f, 1.0f, 126 1.0f, 1.0f, 0.0f, 1.0f, 127 1.0f, 1.0f, 0.0f, 1.0f, 128 1.0f, 1.0f, 0.0f, 1.0f, 129 130 // Top face (cyan) 131 0.0f, 1.0f, 1.0f, 1.0f, 132 0.0f, 1.0f, 1.0f, 1.0f, 133 0.0f, 1.0f, 1.0f, 1.0f, 134 0.0f, 1.0f, 1.0f, 1.0f, 135 0.0f, 1.0f, 1.0f, 1.0f, 136 0.0f, 1.0f, 1.0f, 1.0f, 137 138 // Bottom face (magenta) 139 1.0f, 0.0f, 1.0f, 1.0f, 140 1.0f, 0.0f, 1.0f, 1.0f, 141 1.0f, 0.0f, 1.0f, 1.0f, 142 1.0f, 0.0f, 1.0f, 1.0f, 143 1.0f, 0.0f, 1.0f, 1.0f, 144 1.0f, 0.0f, 1.0f, 1.0f 145 }; 146 147 final float[] cubeTextureCoordinate = 148 { 149 // Front face 150 0.0f, 0.0f, 151 0.0f, 1.0f, 152 1.0f, 0.0f, 153 0.0f, 1.0f, 154 1.0f, 1.0f, 155 1.0f, 0.0f, 156 157 // Right face 158 0.0f, 0.0f, 159 0.0f, 1.0f, 160 1.0f, 0.0f, 161 0.0f, 1.0f, 162 1.0f, 1.0f, 163 1.0f, 0.0f, 164 165 // Back face 166 0.0f, 0.0f, 167 0.0f, 1.0f, 168 1.0f, 0.0f, 169 0.0f, 1.0f, 170 1.0f, 1.0f, 171 1.0f, 0.0f, 172 173 // Left face 174 0.0f, 0.0f, 175 0.0f, 1.0f, 176 1.0f, 0.0f, 177 0.0f, 1.0f, 178 1.0f, 1.0f, 179 1.0f, 0.0f, 180 181 // Top face 182 0.0f, 0.0f, 183 0.0f, 1.0f, 184 1.0f, 0.0f, 185 0.0f, 1.0f, 186 1.0f, 1.0f, 187 1.0f, 0.0f, 188 189 // Bottom face 190 0.0f, 0.0f, 191 0.0f, 1.0f, 192 1.0f, 0.0f, 193 0.0f, 1.0f, 194 1.0f, 1.0f, 195 1.0f, 0.0f 196 }; 197 198 mCubePositions = ByteBuffer.allocateDirect(cubePosition.length * BYTES_PER_FLOAT) 199 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 200 mCubePositions.put(cubePosition).position(0); 201 mCubeColors = ByteBuffer.allocateDirect(cubeColor.length * BYTES_PER_FLOAT) 202 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 203 mCubeColors.put(cubeColor).position(0); 204 mCubeTextureCoordinates = ByteBuffer.allocateDirect(cubeTextureCoordinate.length * BYTES_PER_FLOAT) 205 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 206 mCubeTextureCoordinates.put(cubeTextureCoordinate).position(0); 207 } 208 209 @Override 210 public void onDrawFrame(GL10 gl) { 211 // TODO Auto-generated method stub 212 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 213 long time = SystemClock.uptimeMillis() % 10000L; 214 float angleInDegrees = (360.0f / 10000.0f) * ((int) time); 215 GLES20.glUseProgram(mProgramHandle); 216 mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix"); 217 mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture"); 218 mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); 219 mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color"); 220 mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); 221 222 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 223 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); 224 GLES20.glUniform1i(mTextureUniformHandle, 0); 225 226 Matrix.setIdentityM(mModelMatrix, 0); 227 Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -5.0f); 228 Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f); 229 drawCube(mCubePositions, mCubeColors, mCubeTextureCoordinates); 230 } 231 232 private void drawCube(final FloatBuffer cubePositionsBuffer, final FloatBuffer cubeColorsBuffer, final FloatBuffer cubeTextureCoordinatesBuffer) 233 { 234 cubePositionsBuffer.position(0); 235 GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, cubePositionsBuffer); 236 GLES20.glEnableVertexAttribArray(mPositionHandle); 237 238 cubeColorsBuffer.position(0); 239 GLES20.glVertexAttribPointer(mColorHandle, COLOR_DATA_SIZE, GLES20.GL_FLOAT, false, 0, cubeColorsBuffer); 240 GLES20.glEnableVertexAttribArray(mColorHandle); 241 242 cubeTextureCoordinatesBuffer.position(0); 243 GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINATE_DATA_SIZE, 244 GLES20.GL_FLOAT, false, 0, cubeTextureCoordinatesBuffer); 245 GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); 246 247 Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); 248 Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); 249 GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0); 250 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); 251 } 252 253 @Override 254 public void onSurfaceChanged(GL10 gl, int width, int height) { 255 // TODO Auto-generated method stub 256 GLES20.glViewport(0, 0, width, height); 257 258 final float ratio = (float) width / height; 259 final float left = -ratio; 260 final float right = ratio; 261 final float bottom = -1.0f; 262 final float top = 1.0f; 263 final float near = 1.0f; 264 final float far = 10.0f; 265 266 Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far); 267 } 268 269 @Override 270 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 271 // TODO Auto-generated method stub 272 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 273 GLES20.glEnable(GLES20.GL_CULL_FACE); 274 GLES20.glEnable(GLES20.GL_DEPTH_TEST); 275 // Position the eye behind the origin. 276 final float eyeX = 0.0f; 277 final float eyeY = 0.0f; 278 final float eyeZ = -0.5f; 279 280 // We are looking toward the distance 281 final float lookX = 0.0f; 282 final float lookY = 0.0f; 283 final float lookZ = -5.0f; 284 285 // Set our up vector. This is where our head would be pointing were we holding the camera. 286 final float upX = 0.0f; 287 final float upY = 1.0f; 288 final float upZ = 0.0f; 289 290 // Set the view matrix. This matrix can be said to represent the camera position. 291 // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and 292 // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose. 293 Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); 294 295 final String vertexShader = getVertexShader(); 296 final String fragmentShader = getFragmentShader(); 297 298 final int vertexShaderHandle = compileShader(GLES20.GL_VERTEX_SHADER, vertexShader); 299 final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader); 300 301 mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, 302 new String[]{"a_Position", "a_Color", "a_TexCoordinate"}); 303 304 mTextureDataHandle = ToolsUtil.loadTexture(mContext, R.drawable.bumpy_bricks_public_domain); 305 306 } 307 308 private String getVertexShader() 309 { 310 final String vertexShader = 311 "uniform mat4 u_MVPMatrix; \n" // A constant representing the combined model/view/projection matrix. 312 313 + "attribute vec4 a_Position; \n" // Per-vertex position information we will pass in. 314 + "attribute vec4 a_Color; \n" // Per-vertex color information we will pass in. 315 + "attribute vec2 a_TexCoordinate;\n" // Per-vertex texture coordinate information we will pass in. 316 317 + "varying vec4 v_Color; \n" // This will be passed into the fragment shader. 318 + "varying vec2 v_TexCoordinate; \n" // This will be passed into the fragment shader. 319 320 + "void main() \n" // The entry point for our vertex shader. 321 + "{ \n" 322 + " v_Color = a_Color; \n" // Pass the color through to the fragment shader. 323 // It will be interpolated across the triangle. 324 + " v_TexCoordinate = a_TexCoordinate;\n"// Pass through the texture coordinate. 325 + " gl_Position = u_MVPMatrix \n" // gl_Position is a special variable used to store the final position. 326 + " * a_Position; \n" // Multiply the vertex by the matrix to get the final point in 327 + "} \n"; // normalized screen coordinates. \n"; 328 329 return vertexShader; 330 } 331 332 private String getFragmentShader() 333 { 334 final String fragmentShader = 335 "precision mediump float; \n" // Set the default precision to medium. We don't need as high of a 336 // precision in the fragment shader. 337 + "uniform sampler2D u_Texture; \n" // The input texture. 338 + "varying vec4 v_Color; \n" // This is the color from the vertex shader interpolated across the 339 // triangle per fragment. 340 + "varying vec2 v_TexCoordinate; \n" // Interpolated texture coordinate per fragment. 341 342 + "void main() \n" // The entry point for our fragment shader. 343 + "{ \n" 344 + " gl_FragColor = v_Color * texture2D(u_Texture, v_TexCoordinate); \n" // Pass the color directly through the pipeline. 345 + "} \n"; 346 347 return fragmentShader; 348 } 349 350 private int compileShader(final int shaderType, final String shaderSource) 351 { 352 int shaderHandle = GLES20.glCreateShader(shaderType); 353 354 if (shaderHandle != 0) 355 { 356 GLES20.glShaderSource(shaderHandle, shaderSource); 357 GLES20.glCompileShader(shaderHandle); 358 359 final int[] compileStatus = new int[1]; 360 GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0); 361 362 if (compileStatus[0] == 0) 363 { 364 Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shaderHandle)); 365 GLES20.glDeleteShader(shaderHandle); 366 shaderHandle = 0; 367 } 368 } 369 370 if (shaderHandle == 0) 371 { 372 throw new RuntimeException("Error creating shader."); 373 } 374 375 return shaderHandle; 376 } 377 378 private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) 379 { 380 int programHandle = GLES20.glCreateProgram(); 381 382 if (programHandle != 0) 383 { 384 GLES20.glAttachShader(programHandle, vertexShaderHandle); 385 GLES20.glAttachShader(programHandle, fragmentShaderHandle); 386 387 if (attributes != null) 388 { 389 final int size = attributes.length; 390 for (int i = 0; i < size; i++) 391 { 392 GLES20.glBindAttribLocation(programHandle, i, attributes[i]); 393 } 394 } 395 396 GLES20.glLinkProgram(programHandle); 397 398 final int[] linkStatus = new int[1]; 399 GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0); 400 401 if (linkStatus[0] == 0) 402 { 403 Log.e(TAG, "Error compiling program: " + GLES20.glGetProgramInfoLog(programHandle)); 404 GLES20.glDeleteProgram(programHandle); 405 programHandle = 0; 406 } 407 } 408 409 if (programHandle == 0) 410 { 411 throw new RuntimeException("Error creating program."); 412 } 413 414 return programHandle; 415 } 416 417 }
在Activity里初始化Renderer时要加参数context.
看效果图: