2011年5月9日 星期一

[ Android OpenGL ES 教學(五)] 貼皮技術

基礎閱讀:[ Android OpenGL ES 教學(四)] 添加顏色轉換相關方法 發表在 http://cheng-min-i-taiwan.blogspot.com/2011/05/android-opengl-es.html
延伸閱讀:[ Android OpenGL ES 教學(六)] 觸控控制 發表在
http://cheng-min-i-taiwan.blogspot.com/2011/05/android-opengl-es_498.html
參考文章:OpenGL ES Tutorial for Android – Part VI – Textures 發表在http://blog.jayway.com/2010/12/30/opengl-es-tutorial-for-android-%E2%80%93-part-vi-textures/

貼皮後的結果


貼皮後的結果(含顏色對映處理)


貼皮除了要載入一張位元圖外,在貼皮質地使用的坐標要特別注意


社員可以試著改變質地坐標中的數值,可以達到放大或縮小位元圖的目的。

OpenGLActivity.java 程式列表
package nkut.cce.smartliving.opengl;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

public class OpenGLActivity extends Activity {
/** Called when the activity is first created. */

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView view = new GLSurfaceView(this);
OpenGLRenderer render = new OpenGLRenderer();

// 載入位元圖
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.icon);
render.setBitmap(bitmap);

view.setRenderer(render);


setContentView(view);
}
}

OpenGLRender.java程式列表

package nkut.cce.smartliving.opengl;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;

public class OpenGLRenderer implements Renderer {
private Square square;
private float angle = 0;

public OpenGLRenderer() {
// 初始化
square = new Square();
}

@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
// 清除螢幕和深度緩衝區
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 以單位矩陣取代目前的矩陣
gl.glLoadIdentity();
// Z軸轉置 10 單位
gl.glTranslatef(0, 0, -10);

// 第一個方形
// 存儲目前陣列
gl.glPushMatrix();
// 反時鐘旋轉
gl.glRotatef(angle, 0, 0, 1);
// 畫出第一個方形
square.draw(gl);
// 復原成最後的矩陣
gl.glPopMatrix();

// 第二個方形
// 存儲目前陣列
gl.glPushMatrix();
// 在移動前先旋轉, 讓第二個方形圍繞著第一個方形旋轉
gl.glRotatef(-angle, 0, 0, 1);
// 移動第二個方形
gl.glTranslatef(2, 0, 0);
// 調整其大小為第一個方形的一半
gl.glScalef(.5f, .5f, .5f);
// 畫出第二個方形
square.draw(gl);

// 第三個方形
// 存儲目前陣列
gl.glPushMatrix();
// 讓第三個方形圍繞著第二個方形旋轉
gl.glRotatef(-angle, 0, 0, 1);
// 移動第三個方形
gl.glTranslatef(2, 0, 0);
// 調整其大小為第二個方形的一半
gl.glScalef(.5f, .5f, .5f);
// 以自己為中心旋轉
gl.glRotatef(angle * 10, 0, 0, 1);
// 畫出第三個方形.
square.draw(gl);

// 復原成第三個方形前的矩陣
gl.glPopMatrix();
// 復原成第二個方形前的矩陣.
gl.glPopMatrix();

// 增加角度
angle++;
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
// 設定新視域視窗的大小
gl.glViewport(0, 0, width, height);
// 選擇投射的陣列模式
gl.glMatrixMode(GL10.GL_PROJECTION);
// 重設投射陣
gl.glLoadIdentity();
// 計算視窗的寬高比率
GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,
100.0f);
// 選擇MODELVIEW陣列
gl.glMatrixMode(GL10.GL_MODELVIEW);
// 重設MODELVIEW陣列
gl.glLoadIdentity();
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
// 設定背景顏色為黑色, 格式是RGBA
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
// 設定流暢的陰影模式
gl.glShadeModel(GL10.GL_SMOOTH);
// 深度緩區的設定
gl.glClearDepthf(1.0f);
// 啟動深度的測試
gl.glEnable(GL10.GL_DEPTH_TEST);
// GL_LEQUAL深度函式測試
gl.glDepthFunc(GL10.GL_LEQUAL);
// 設定很好的角度計算模式
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}

public void setBitmap(Bitmap bitmap) {
// TODO Auto-generated method stub
square.setBitmap(bitmap);
}

}

square.java程式列表

package nkut.cce.smartliving.opengl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.opengl.GLUtils;

public class Square {
// 點的陣列
private float vertices[] = { -1.0f, 1.0f, 0.0f, // 0, 左上角
-1.0f, -1.0f, 0.0f, // 1, 左下角
1.0f, -1.0f, 0.0f, // 2, 右下角
1.0f, 1.0f, 0.0f, // 3, 右上角
};

// 將顏色資訊對應到點陣列上
float[] colors = { 1f, 0f, 0f, 1f, // 左上角 0 red
0f, 1f, 0f, 1f, // 左下角 1 green
0f, 0f, 1f, 1f, // 右下角 2 blue
1f, 0f, 1f, 1f, // 右上角 3 magenta
};

// 質地坐標
float texture[] = { 0.0f, 0.0f, //
0.0f, 1.0f, //
1.0f, 1.0f, //
1.0f, 0.0f, //
};

// 連接點的次序
private short[] indices = { 0, 1, 2, 0, 2, 3 };

// 點的緩衝區
private FloatBuffer vertexBuffer;

// 索引值緩衝區
private ShortBuffer indexBuffer;

// 顏色緩衝區
private FloatBuffer colorBuffer;

// 質地緩衝區
private FloatBuffer textureBuffer;

Bitmap bitmap;


public Square() {
// 浮點數是4位元組因此需要把點陣列長度乘以4
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);

// 短整數是2位元組因此需要把點陣列長度乘以2
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
indexBuffer = ibb.asShortBuffer();
indexBuffer.put(indices);
indexBuffer.position(0);

// 浮點數是4位元組,顏色(RGBA)
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
cbb.order(ByteOrder.nativeOrder());
colorBuffer = cbb.asFloatBuffer();
colorBuffer.put(colors);
colorBuffer.position(0);

ByteBuffer byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
textureBuffer = byteBuf.asFloatBuffer();
textureBuffer.put(texture);
textureBuffer.position(0);

}

/**
* 畫圖函式
*
* @param gl
*/
public void draw(GL10 gl) {
// 逆時鐘
gl.glFrontFace(GL10.GL_CCW);
// 啟動CULL_FACE
gl.glEnable(GL10.GL_CULL_FACE);
// 刪除多邊形的背景
gl.glCullFace(GL10.GL_BACK);

// 啟動點的緩衝區
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 指定位置和資料格式
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

// 在渲染期間啟用顏色緩衝區
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

// 指定顏色緩衝區。
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);

// 建立質地陣列
int[] textures = new int[1];
// 告訴OpenGL產生第幾個質地
gl.glGenTextures(1, textures, 0);

// 指定要用那一質地
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

// 載入質地位元圖
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

// 啟動質地功能
gl.glEnable(GL10.GL_TEXTURE_2D);

// 使用UV坐標
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

// 指定質地緩衝區
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
// 以三點劃出三角形
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
GL10.GL_UNSIGNED_SHORT, indexBuffer);

// 除能點的緩衝區
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
// 除能CULL_FACE
gl.glDisable(GL10.GL_CULL_FACE);

// 除能UV坐標
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

// 除能質地的使用
gl.glDisable(GL10.GL_TEXTURE_2D);
}

public void setBitmap(Bitmap bitmap) {
// TODO Auto-generated method stub
this.bitmap = bitmap;
}
}


7 則留言:

  1. 想請問一下我RUN在AVD上面的結果跟作者的結果是相似的。可是RUN在實體手機上卻沒看到位元圖,只看到跟教學四一樣的結果,是否可請教問題出在哪邊?

    回覆刪除
  2. 您好, 我也遇到上面那位gama大大一樣的問題,
    可否請教問題有可能出在哪裡呢?

    回覆刪除
    回覆
    1. 可否寄原始檔給我測試

      刪除
    2. 我目前把APK給多隻手機測試, 目前都是無法顯示圖片的狀態...(只有白框框)
      google了一下, 有幾個可能性都有排除(圖片邊長要是2的N次方, 副檔名bmp)

      刪除
    3. 您好, 我查到一個可能性, 目前我的圖片有正確顯示了!
      貌似是少了個參數值, 導致圖片無法正確顯示!
      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_NEAREST);

      刪除