Android OpenGL ES 2.0+ 使用可程式化管線。核心類別:
GLSurfaceView:管理 EGL 上下文、渲染執行緒、Surface 生命週期。GLSurfaceView.Renderer 介面:onSurfaceCreated、onSurfaceChanged、onDrawFrame。GL10 / GLES20 / GLES30:對應 OpenGL ES 版本的 Java 綁定。package com.example.opengl;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class TextureRenderer implements GLSurfaceView.Renderer {
private final Context context;
private final Square square = new Square();
public TextureRenderer(Context context) {
this.context = context;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 載入紋理
square.loadGLTexture(gl, context);
gl.glEnable(GL10.GL_TEXTURE_2D); // 啟用 2D 紋理
gl.glShadeModel(GL10.GL_SMOOTH); // 平滑著色
gl.glClearColor(0f, 0f, 0f, 0.5f); // 黑色半透明背景
gl.glClearDepthf(1f); // 深度緩衝初始值
gl.glEnable(GL10.GL_DEPTH_TEST); // 啟用深度測試
gl.glDepthFunc(GL10.GL_LEQUAL); // 深度函數
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); // 透視修正品質
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if (height == 0) height = 1;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45f, (float) width / height, 0.1f, 100f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glTranslatef(0f, 0f, -5f); // 向螢幕內移 5 單位
square.draw(gl);
}
}
package com.example.opengl;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import javax.microedition.khronos.opengles.GL10;
import java.nio.*;
public class Square {
private FloatBuffer vertexBuffer, textureBuffer;
private final int[] textureHandle = new int[1];
// 頂點:逆時針順序 (預設正面)
private final float[] vertices = {
-1f, -1f, 0f, // 左下
1f, -1f, 0f, // 右下
-1f, 1f, 0f, // 左上
1f, 1f, 0f // 右上
};
// 紋理座標 (S,T) 對應頂點順序
private final float[] texCoords = {
0f, 1f, // 左下 -> 紋理左下
1f, 1f, // 右下 -> 紋理右下
0f, 0f, // 左上 -> 紋理左上
1f, 0f // 右上 -> 紋理右上
};
public Square() {
vertexBuffer = createFloatBuffer(vertices);
textureBuffer = createFloatBuffer(texCoords);
}
private FloatBuffer createFloatBuffer(float[] arr) {
ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer fb = bb.asFloatBuffer();
fb.put(arr);
fb.position(0);
return fb;
}
public void loadGLTexture(GL10 gl, Context context) {
// 1. 解碼圖片 (需放在 res/drawable-nodpi/ 避免縮放)
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.texture);
// 2. 產生紋理 ID
gl.glGenTextures(1, textureHandle, 0);
// 3. 綁定紋理
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureHandle[0]);
// 4. 設定濾波模式
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// 5. 上傳圖片資料到 GPU
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// 6. 釋放 Bitmap 記憶體
bitmap.recycle();
}
public void draw(GL10 gl) {
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureHandle[0]);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glFrontFace(GL10.GL_CCW); // 逆時針為正面
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
// TRIANGLE_STRIP: 0,1,2,3 形成兩三角形
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
}
public class MainActivity extends Activity {
private GLSurfaceView glView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
glView = new GLSurfaceView(this);
glView.setEGLContextClientVersion(1); // ES 1.x
glView.setRenderer(new TextureRenderer(this));
setContentView(glView);
}
@Override protected void onResume() { super.onResume(); glView.onResume(); }
@Override protected void onPause() { glView.onPause(); super.onPause(); }
}
GLUtils.texImage2D 可能失敗或自動縮放 (視驅動)。drawable-nodpi/ 避免系統按螢幕密度縮放導致尺寸非 2 的冪次。GL_TRIANGLE_STRIP 配合正確頂點序可高效畫四邊形。GLES20、glUseProgram、glUniform、glVertexAttribPointer,彈性大得多但樣板碼多。Android OpenGL ES 紋理映射核心流程:建立幾何緩衝區 → 載入 Bitmap → 產生/綁定紋理 ID → 設定參數 → 上傳像素 → 繪製時綁定紋理並啟用座標陣列。理解固定功能管線 (ES 1.x) 有助於過渡到可程式化管線 (ES 2.0+),後者是現代圖形開發的基礎。