初學Android,OpenGL ES之使用紋理(八十三)
阿新 • • 發佈:2019-01-06
在網上發現這些講紋理的文章,非常不錯
android 遊戲導引(4. 簡單紋理貼圖)
Android OpenGL es 紋理座標設定與貼圖規則
Android OpenGL | ES給立方體進行紋理對映
這篇的例子也是立方體的紋理,不過是用手指劃過讓立方體旋轉
使用紋理的重要程式碼如下
private void loadTexture(GL10 gl) { Bitmap bitmap = null; try { // 載入點陣圖 bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.sand); int[] textures = new int[1]; // 指定生成N個紋理(第一個引數指定生成1個紋理), // textures陣列將負責儲存所有紋理的代號。 gl.glGenTextures(1, textures, 0); // 獲取textures紋理陣列中的第一個紋理 texture = textures[0]; // 通知OpenGL將texture紋理繫結到GL10.GL_TEXTURE_2D目標中 gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); // 設定紋理被縮小(距離視點很遠時被縮小)時候的濾波方式 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); // 設定紋理被放大(距離視點很近時被方法)時候的濾波方式 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); // 設定在橫向、縱向上都是平鋪紋理 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT); // 載入點陣圖生成紋理 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); } finally { // 生成紋理之後,回收點陣圖 if (bitmap != null) bitmap.recycle(); } }
紋理的裝載則是在方法void onSurfaceCreated(GL10 gl, EGLConfig config) 中
在Renderer的 void onDrawFrame(GL10 gl)方法中要啟用紋理的陣列資料,座標資料,執行紋理貼圖
// 啟用貼圖座標陣列資料 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // 設定貼圖的的座標資料 gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, cubeTexturesBuffer); // 執行紋理貼圖 gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
完整程式碼如下
public class Texture3D extends Activity implements OnGestureListener { // 定義旋轉角度 private float anglex = 0f; private float angley = 0f; static final float ROTATE_FACTOR = 60; // 定義手勢檢測器例項 GestureDetector detector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 建立一個GLSurfaceView,用於顯示OpenGL繪製的圖形 GLSurfaceView glView = new GLSurfaceView(this); // 建立GLSurfaceView的內容繪製器 MyRenderer myRender = new MyRenderer(this); // 為GLSurfaceView設定繪製器 glView.setRenderer(myRender); setContentView(glView); // 建立手勢檢測器 detector = new GestureDetector(this); } @Override public boolean onTouchEvent(MotionEvent me) { // 將該Activity上的觸碰事件交給GestureDetector處理 return detector.onTouchEvent(me); } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { velocityX = velocityX > 4000 ? 4000 : velocityX; velocityX = velocityX < -4000 ? -4000 : velocityX; velocityY = velocityY > 4000 ? 4000 : velocityY; velocityY = velocityY < -4000 ? -4000 : velocityY; // 根據橫向上的速度計算沿Y軸旋轉的角度 angley += velocityX * ROTATE_FACTOR / 4000; // 根據縱向上的速度計算沿X軸旋轉的角度 anglex += velocityY * ROTATE_FACTOR / 4000; return true; } @Override public boolean onDown(MotionEvent arg0) { return false; } @Override public void onLongPress(MotionEvent event) { } @Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) { return false; } @Override public void onShowPress(MotionEvent event) { } @Override public boolean onSingleTapUp(MotionEvent event) { return false; } public class MyRenderer implements Renderer { // 立方體的頂點座標(一共是36個頂點,組成12個三角形) private float[] cubeVertices = { -0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f, -0.6f, -0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, -0.6f, }; // 定義立方體所需要的6個面(一共是12個三角形所需的頂點) private byte[] cubeFacets = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, }; // 定義紋理貼圖的座標資料 private float[] cubeTextures = { 1.0000f, 1.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f }; private Context context; private FloatBuffer cubeVerticesBuffer; private ByteBuffer cubeFacetsBuffer; private FloatBuffer cubeTexturesBuffer; // 定義本程式所使用的紋理 private int texture; public MyRenderer(Context main) { this.context = main; // 將立方體的頂點位置資料陣列包裝成FloatBuffer; cubeVerticesBuffer = FloatBuffer.wrap(cubeVertices); // 將立方體的6個面(12個三角形)的陣列包裝成ByteBuffer cubeFacetsBuffer = ByteBuffer.wrap(cubeFacets); // 將立方體的紋理貼圖的座標資料包裝成FloatBuffer cubeTexturesBuffer = FloatBuffer.wrap(cubeTextures); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // 關閉抗抖動 gl.glDisable(GL10.GL_DITHER); // 設定系統對透視進行修正 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glClearColor(0, 0, 0, 0); // 設定陰影平滑模式 gl.glShadeModel(GL10.GL_SMOOTH); // 啟用深度測試 gl.glEnable(GL10.GL_DEPTH_TEST); // 設定深度測試的型別 gl.glDepthFunc(GL10.GL_LEQUAL); // 啟用2D紋理貼圖 gl.glEnable(GL10.GL_TEXTURE_2D); // 裝載紋理 loadTexture(gl); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { // 設定3D視窗的大小及位置 gl.glViewport(0, 0, width, height); // 將當前矩陣模式設為投影矩陣 gl.glMatrixMode(GL10.GL_PROJECTION); // 初始化單位矩陣 gl.glLoadIdentity(); // 計算透視視窗的寬度、高度比 float ratio = (float) width / height; // 呼叫此方法設定透視視窗的空間大小。 gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); } public void onDrawFrame(GL10 gl) { // 清除螢幕快取和深度快取 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // 啟用頂點座標資料 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // 啟用貼圖座標陣列資料 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // 設定當前矩陣模式為模型檢視。 gl.glMatrixMode(GL10.GL_MODELVIEW); // --------------------繪製第一個圖形--------------------- gl.glLoadIdentity(); // 把繪圖中心移入螢幕2個單位 gl.glTranslatef(0f, 0.0f, -2.0f); // 旋轉圖形 gl.glRotatef(angley, 0, 1, 0); gl.glRotatef(anglex, 1, 0, 0); // 設定頂點的位置資料 gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeVerticesBuffer); // 設定貼圖的的座標資料 gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, cubeTexturesBuffer); // 執行紋理貼圖 gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); // 按cubeFacetsBuffer指定的面繪製三角形 gl.glDrawElements(GL10.GL_TRIANGLES, cubeFacetsBuffer.remaining(), GL10.GL_UNSIGNED_BYTE, cubeFacetsBuffer); // 繪製結束 gl.glFinish(); // 禁用頂點、紋理座標陣列 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // 遞增角度值以便每次以不同角度繪製 } private void loadTexture(GL10 gl) { Bitmap bitmap = null; try { // 載入點陣圖 bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.sand); int[] textures = new int[1]; // 指定生成N個紋理(第一個引數指定生成1個紋理), // textures陣列將負責儲存所有紋理的代號。 gl.glGenTextures(1, textures, 0); // 獲取textures紋理陣列中的第一個紋理 texture = textures[0]; // 通知OpenGL將texture紋理繫結到GL10.GL_TEXTURE_2D目標中 gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); // 設定紋理被縮小(距離視點很遠時被縮小)時候的濾波方式 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); // 設定紋理被放大(距離視點很近時被方法)時候的濾波方式 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); // 設定在橫向、縱向上都是平鋪紋理 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT); // 載入點陣圖生成紋理 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); } finally { // 生成紋理之後,回收點陣圖 if (bitmap != null) bitmap.recycle(); } } } }