画完立方体后,在立方体旁边加了一个灯光point light,也加上了旋转效果。
这里新加了normal data,用来作光计算的。
这里用的是per vertex lighting,所以光移动到立方体顶点位置的时候,可以看到,立方体平面的对边顶点沿线周围部分会特别亮。
对光的计算都在物体的vertex shader里。
Test3Renderer.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 android.opengl.GLES20; 11 import android.opengl.Matrix; 12 import android.opengl.GLSurfaceView; 13 import android.os.SystemClock; 14 import android.util.Log; 15 16 public class Test3Renderer implements GLSurfaceView.Renderer 17 { 18 private static final String TAG = "Test3Renderer"; 19 private static final int BYTES_PER_FLOAT = 4; 20 private final FloatBuffer mCubePositions; 21 private final FloatBuffer mCubeColors; 22 private final FloatBuffer mCubeNormals; 23 24 private float[] mMVPMatrix = new float[16]; 25 private float[] mViewMatrix = new float[16]; 26 private float[] mModelMatrix = new float[16]; 27 private float[] mProjectionMatrix = new float[16]; 28 private float[] mLightModelMatrix = new float[16]; 29 30 private final float[] mLightPosInModelSpace = new float[] {0.0f, 0.0f, 0.0f, 1.0f}; 31 private final float[] mLightPosInWorldSpace = new float[4]; 32 private final float[] mLightPosInEyeSpace = new float[4]; 33 34 private int mMVPMatrixHandle; 35 private int mMVMatrixHandle; 36 private int mPositionHandle; 37 private int mLightPosHandle; 38 private int mColorHandle; 39 private int mNormalHandle; 40 private int mPerVertexProgramHandle; 41 private int mPointProgramHandle; 42 private final int POSITION_DATA_SIZE = 3; 43 private final int COLOR_DATA_SIZE = 4; 44 private final int NORMAL_DATA_SIZE = 3; 45 46 public Test3Renderer() 47 { 48 final float cubePosition[] = 49 { 50 // Front face 51 -1.0f, 1.0f, 1.0f, 52 -1.0f, -1.0f, 1.0f, 53 1.0f, 1.0f, 1.0f, 54 -1.0f, -1.0f, 1.0f, 55 1.0f, -1.0f, 1.0f, 56 1.0f, 1.0f, 1.0f, 57 58 // Right face 59 1.0f, 1.0f, 1.0f, 60 1.0f, -1.0f, 1.0f, 61 1.0f, 1.0f, -1.0f, 62 1.0f, -1.0f, 1.0f, 63 1.0f, -1.0f, -1.0f, 64 1.0f, 1.0f, -1.0f, 65 66 // Back face 67 1.0f, 1.0f, -1.0f, 68 1.0f, -1.0f, -1.0f, 69 -1.0f, 1.0f, -1.0f, 70 1.0f, -1.0f, -1.0f, 71 -1.0f, -1.0f, -1.0f, 72 -1.0f, 1.0f, -1.0f, 73 74 // Left face 75 -1.0f, 1.0f, -1.0f, 76 -1.0f, -1.0f, -1.0f, 77 -1.0f, 1.0f, 1.0f, 78 -1.0f, -1.0f, -1.0f, 79 -1.0f, -1.0f, 1.0f, 80 -1.0f, 1.0f, 1.0f, 81 82 // Top face 83 -1.0f, 1.0f, -1.0f, 84 -1.0f, 1.0f, 1.0f, 85 1.0f, 1.0f, -1.0f, 86 -1.0f, 1.0f, 1.0f, 87 1.0f, 1.0f, 1.0f, 88 1.0f, 1.0f, -1.0f, 89 90 // Bottom face 91 1.0f, -1.0f, -1.0f, 92 1.0f, -1.0f, 1.0f, 93 -1.0f, -1.0f, -1.0f, 94 1.0f, -1.0f, 1.0f, 95 -1.0f, -1.0f, 1.0f, 96 -1.0f, -1.0f, -1.0f, 97 }; 98 99 final float[] cubeColor = 100 { 101 // Front face (red) 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 1.0f, 0.0f, 0.0f, 1.0f, 106 1.0f, 0.0f, 0.0f, 1.0f, 107 1.0f, 0.0f, 0.0f, 1.0f, 108 109 // Right face (green) 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 0.0f, 1.0f, 0.0f, 1.0f, 114 0.0f, 1.0f, 0.0f, 1.0f, 115 0.0f, 1.0f, 0.0f, 1.0f, 116 117 // Back face (blue) 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 0.0f, 0.0f, 1.0f, 1.0f, 122 0.0f, 0.0f, 1.0f, 1.0f, 123 0.0f, 0.0f, 1.0f, 1.0f, 124 125 // Left face (yellow) 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 1.0f, 1.0f, 0.0f, 1.0f, 130 1.0f, 1.0f, 0.0f, 1.0f, 131 1.0f, 1.0f, 0.0f, 1.0f, 132 133 // Top face (cyan) 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 0.0f, 1.0f, 1.0f, 1.0f, 138 0.0f, 1.0f, 1.0f, 1.0f, 139 0.0f, 1.0f, 1.0f, 1.0f, 140 141 // Bottom face (magenta) 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 1.0f, 0.0f, 1.0f, 1.0f, 146 1.0f, 0.0f, 1.0f, 1.0f, 147 1.0f, 0.0f, 1.0f, 1.0f 148 }; 149 150 final float[] cubeNormal = 151 { 152 // Front face 153 0.0f, 0.0f, 1.0f, 154 0.0f, 0.0f, 1.0f, 155 0.0f, 0.0f, 1.0f, 156 0.0f, 0.0f, 1.0f, 157 0.0f, 0.0f, 1.0f, 158 0.0f, 0.0f, 1.0f, 159 160 // Right face 161 1.0f, 0.0f, 0.0f, 162 1.0f, 0.0f, 0.0f, 163 1.0f, 0.0f, 0.0f, 164 1.0f, 0.0f, 0.0f, 165 1.0f, 0.0f, 0.0f, 166 1.0f, 0.0f, 0.0f, 167 168 // Back face 169 0.0f, 0.0f, -1.0f, 170 0.0f, 0.0f, -1.0f, 171 0.0f, 0.0f, -1.0f, 172 0.0f, 0.0f, -1.0f, 173 0.0f, 0.0f, -1.0f, 174 0.0f, 0.0f, -1.0f, 175 176 // Left face 177 -1.0f, 0.0f, 0.0f, 178 -1.0f, 0.0f, 0.0f, 179 -1.0f, 0.0f, 0.0f, 180 -1.0f, 0.0f, 0.0f, 181 -1.0f, 0.0f, 0.0f, 182 -1.0f, 0.0f, 0.0f, 183 184 // Top face 185 0.0f, 1.0f, 0.0f, 186 0.0f, 1.0f, 0.0f, 187 0.0f, 1.0f, 0.0f, 188 0.0f, 1.0f, 0.0f, 189 0.0f, 1.0f, 0.0f, 190 0.0f, 1.0f, 0.0f, 191 192 // Bottom face 193 0.0f, -1.0f, 0.0f, 194 0.0f, -1.0f, 0.0f, 195 0.0f, -1.0f, 0.0f, 196 0.0f, -1.0f, 0.0f, 197 0.0f, -1.0f, 0.0f, 198 0.0f, -1.0f, 0.0f 199 }; 200 201 mCubePositions = ByteBuffer.allocateDirect(cubePosition.length * BYTES_PER_FLOAT) 202 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 203 mCubePositions.put(cubePosition).position(0); 204 mCubeColors = ByteBuffer.allocateDirect(cubeColor.length * BYTES_PER_FLOAT) 205 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 206 mCubeColors.put(cubeColor).position(0); 207 mCubeNormals = ByteBuffer.allocateDirect(cubeNormal.length * BYTES_PER_FLOAT) 208 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 209 mCubeNormals.put(cubeNormal).position(0); 210 } 211 212 @Override 213 public void onDrawFrame(GL10 gl) { 214 // TODO Auto-generated method stub 215 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 216 long time = SystemClock.uptimeMillis() % 10000L; 217 float angleInDegrees = (360.0f / 10000.0f) * ((int) time); 218 219 GLES20.glUseProgram(mPerVertexProgramHandle); 220 mMVPMatrixHandle = GLES20.glGetUniformLocation(mPerVertexProgramHandle, "u_MVPMatrix"); 221 mPositionHandle = GLES20.glGetAttribLocation(mPerVertexProgramHandle, "a_Position"); 222 mMVMatrixHandle = GLES20.glGetUniformLocation(mPerVertexProgramHandle, "u_MVMatrix"); 223 mLightPosHandle = GLES20.glGetUniformLocation(mPerVertexProgramHandle, "u_LightPos"); 224 mColorHandle = GLES20.glGetAttribLocation(mPerVertexProgramHandle, "a_Color"); 225 mNormalHandle = GLES20.glGetAttribLocation(mPerVertexProgramHandle, "a_Normal"); 226 227 Matrix.setIdentityM(mLightModelMatrix, 0); 228 Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -5.0f); 229 Matrix.rotateM(mLightModelMatrix, 0, angleInDegrees, 0.0f, 1.0f, 0.0f); 230 Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, 2.0f); 231 232 Matrix.multiplyMV(mLightPosInWorldSpace, 0, mLightModelMatrix, 0, mLightPosInModelSpace, 0); 233 Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0); 234 235 Matrix.setIdentityM(mModelMatrix, 0); 236 Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -5.0f); 237 Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f); 238 drawCube(mCubePositions, mCubeColors, mCubeNormals); 239 240 GLES20.glUseProgram(mPointProgramHandle); 241 drawLight(); 242 } 243 244 private void drawCube(final FloatBuffer cubePositionsBuffer, final FloatBuffer cubeColorsBuffer,final FloatBuffer cubeNormalsBuffer) 245 { 246 cubePositionsBuffer.position(0); 247 GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, cubePositionsBuffer); 248 GLES20.glEnableVertexAttribArray(mPositionHandle); 249 250 cubeColorsBuffer.position(0); 251 GLES20.glVertexAttribPointer(mColorHandle, COLOR_DATA_SIZE, GLES20.GL_FLOAT, false, 0, cubeColorsBuffer); 252 GLES20.glEnableVertexAttribArray(mColorHandle); 253 254 cubeNormalsBuffer.position(0); 255 GLES20.glVertexAttribPointer(mNormalHandle, NORMAL_DATA_SIZE, GLES20.GL_FLOAT, false, 0, cubeNormalsBuffer); 256 GLES20.glEnableVertexAttribArray(mNormalHandle); 257 258 Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); 259 GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0); 260 261 Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); 262 GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0); 263 264 GLES20.glUniform3f(mLightPosHandle, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1], mLightPosInEyeSpace[2]); 265 266 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); 267 } 268 269 private void drawLight() 270 { 271 final int pointMVPMatrixHandle = GLES20.glGetUniformLocation(mPointProgramHandle, "u_MVPMatrix"); 272 final int pointPositionHandle = GLES20.glGetAttribLocation(mPointProgramHandle, "a_Position"); 273 274 GLES20.glVertexAttrib3f(pointPositionHandle, mLightPosInModelSpace[0], mLightPosInModelSpace[1], mLightPosInModelSpace[2]); 275 GLES20.glDisableVertexAttribArray(pointPositionHandle); 276 277 Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mLightModelMatrix, 0); 278 Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); 279 GLES20.glUniformMatrix4fv(pointMVPMatrixHandle, 1, false, mMVPMatrix, 0); 280 281 GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1); 282 } 283 284 @Override 285 public void onSurfaceChanged(GL10 gl, int width, int height) { 286 // TODO Auto-generated method stub 287 GLES20.glViewport(0, 0, width, height); 288 289 final float ratio = (float) width / height; 290 final float left = -ratio; 291 final float right = ratio; 292 final float bottom = -1.0f; 293 final float top = 1.0f; 294 final float near = 1.0f; 295 final float far = 10.0f; 296 297 Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far); 298 } 299 300 @Override 301 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 302 // TODO Auto-generated method stub 303 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 304 GLES20.glEnable(GLES20.GL_CULL_FACE); 305 GLES20.glEnable(GLES20.GL_DEPTH_TEST); 306 // Position the eye behind the origin. 307 final float eyeX = 0.0f; 308 final float eyeY = 0.0f; 309 final float eyeZ = -0.5f; 310 311 // We are looking toward the distance 312 final float lookX = 0.0f; 313 final float lookY = 0.0f; 314 final float lookZ = -5.0f; 315 316 // Set our up vector. This is where our head would be pointing were we holding the camera. 317 final float upX = 0.0f; 318 final float upY = 1.0f; 319 final float upZ = 0.0f; 320 321 // Set the view matrix. This matrix can be said to represent the camera position. 322 // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and 323 // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose. 324 Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); 325 326 final String vertexShader = getVertexShader(); 327 final String fragmentShader = getFragmentShader(); 328 329 final int vertexShaderHandle = compileShader(GLES20.GL_VERTEX_SHADER, vertexShader); 330 final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader); 331 mPerVertexProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, 332 new String[]{"a_Position", "a_Color", "a_Normal"}); 333 334 final String pointVertexShader = 335 "uniform mat4 u_MVPMatrix; \n" 336 + "attribute vec4 a_Position; \n" 337 + "void main() \n" 338 + "{ \n" 339 + " gl_Position = u_MVPMatrix \n" 340 + " * a_Position; \n" 341 + " gl_PointSize = 20.0; \n" 342 + "} \n"; 343 344 final String pointFragmentShader = 345 "precision mediump float; \n" 346 + "void main() \n" 347 + "{ \n" 348 + " gl_FragColor = vec4(1.0, \n" 349 + " 1.0, 1.0, 1.0); \n" 350 + "} \n"; 351 352 final int pointVertexShaderHandle = compileShader(GLES20.GL_VERTEX_SHADER, pointVertexShader); 353 final int pointFragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, pointFragmentShader); 354 mPointProgramHandle = createAndLinkProgram(pointVertexShaderHandle, pointFragmentShaderHandle, 355 new String[]{"a_Position"}); 356 357 } 358 359 private String getVertexShader() 360 { 361 final String vertexShader = 362 "uniform mat4 u_MVPMatrix; \n" // A constant representing the combined model/view/projection matrix. 363 + "uniform mat4 u_MVMatrix; \n" // A constant representing the combined model/view matrix. 364 + "uniform vec3 u_LightPos; \n" // The position of the light in eye space. 365 366 + "attribute vec4 a_Position; \n" // Per-vertex position information we will pass in. 367 + "attribute vec4 a_Color; \n" // Per-vertex color information we will pass in. 368 + "attribute vec3 a_Normal; \n" // Per-vertex normal information we will pass in. 369 370 + "varying vec4 v_Color; \n" // This will be passed into the fragment shader. 371 372 + "void main() \n" // The entry point for our vertex shader. 373 + "{ \n" 374 // Transform the vertex into eye space. 375 + " vec3 modelViewVertex = vec3(u_MVMatrix * a_Position); \n" 376 // Transform the normal's orientation into eye space. 377 + " vec3 modelViewNormal = vec3(u_MVMatrix * vec4(a_Normal, 0.0)); \n" 378 // Will be used for attenuation. 379 + " float distance = length(u_LightPos - modelViewVertex); \n" 380 // Get a lighting direction vector from the light to the vertex. 381 + " vec3 lightVector = normalize(u_LightPos - modelViewVertex); \n" 382 // Calculate the dot product of the light vector and vertex normal. If the normal and light vector are 383 // pointing in the same direction then it will get max illumination. 384 + " float diffuse = max(dot(modelViewNormal, lightVector), 0.1); \n" 385 // Attenuate the light based on distance. 386 + " diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance))); \n" 387 // Multiply the color by the illumination level. It will be interpolated across the triangle. 388 + " v_Color = a_Color * diffuse; \n" 389 // gl_Position is a special variable used to store the final position. 390 // Multiply the vertex by the matrix to get the final point in normalized screen coordinates. 391 + " gl_Position = u_MVPMatrix * a_Position; \n" 392 + "} \n"; 393 394 return vertexShader; 395 } 396 397 private String getFragmentShader() 398 { 399 final String fragmentShader = 400 "precision mediump float; \n" // Set the default precision to medium. We don't need as high of a 401 // precision in the fragment shader. 402 + "varying vec4 v_Color; \n" // This is the color from the vertex shader interpolated across the 403 // triangle per fragment. 404 + "void main() \n" // The entry point for our fragment shader. 405 + "{ \n" 406 + " gl_FragColor = v_Color; \n" // Pass the color directly through the pipeline. 407 + "} \n"; 408 409 return fragmentShader; 410 } 411 412 private int compileShader(final int shaderType, final String shaderSource) 413 { 414 int shaderHandle = GLES20.glCreateShader(shaderType); 415 416 if (shaderHandle != 0) 417 { 418 GLES20.glShaderSource(shaderHandle, shaderSource); 419 GLES20.glCompileShader(shaderHandle); 420 421 final int[] compileStatus = new int[1]; 422 GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0); 423 424 if (compileStatus[0] == 0) 425 { 426 Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shaderHandle)); 427 GLES20.glDeleteShader(shaderHandle); 428 shaderHandle = 0; 429 } 430 } 431 432 if (shaderHandle == 0) 433 { 434 throw new RuntimeException("Error creating shader."); 435 } 436 437 return shaderHandle; 438 } 439 440 private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) 441 { 442 int programHandle = GLES20.glCreateProgram(); 443 444 if (programHandle != 0) 445 { 446 GLES20.glAttachShader(programHandle, vertexShaderHandle); 447 GLES20.glAttachShader(programHandle, fragmentShaderHandle); 448 449 if (attributes != null) 450 { 451 final int size = attributes.length; 452 for (int i = 0; i < size; i++) 453 { 454 GLES20.glBindAttribLocation(programHandle, i, attributes[i]); 455 } 456 } 457 458 GLES20.glLinkProgram(programHandle); 459 460 final int[] linkStatus = new int[1]; 461 GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0); 462 463 if (linkStatus[0] == 0) 464 { 465 Log.e(TAG, "Error compiling program: " + GLES20.glGetProgramInfoLog(programHandle)); 466 GLES20.glDeleteProgram(programHandle); 467 programHandle = 0; 468 } 469 } 470 471 if (programHandle == 0) 472 { 473 throw new RuntimeException("Error creating program."); 474 } 475 476 return programHandle; 477 } 478 }
这个程序里,把生成物体vertex shader 和 gragment shader的source string单独提取出了两个方法,便于使用。
分别是getVertexShader和getFragmentShader。
同样,把编译shader也提取成方法compileShader。
还有生成和连接program的方法createAndLinkProgram。
看下效果图: