Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染
本文參考了王剛的《瘋狂Android講義(第3版)》P554-P559
要求:利用OpenGL ES繪制一個三棱錐,并對每個面進行紋理貼圖,每個面使用不同的圖片進行渲染。
環境:Android Studio 3.6.1,gradle版本為5.6.4,OpenGL使用1x版本,新建工程后需在res/drawable目錄下放4張圖片img1、img2、img3、img4(建議圖片長寬均是2的n次方,如果不符合則系統會自動調節,我使用的圖片長寬是256×256)。
運行結果:
三棱錐多紋理貼圖運行結果?主要步驟:
1.在MainActivity.java文件自定義內部公共類MyRenderer實現接口Renderer(也可新建Java文件來實現)。
2.定義三棱錐的頂點坐標、紋理坐標、幾個需要用到的Buffer及其他數據等。
// 定義三棱椎的4個頂點 private float[] taperVertices = new float[]{0.0f, 0.5f, 0.0f, //0-0.5f, -0.5f, -0.2f, //10.5f, -0.5f, -0.2f, //20.0f, -0.2f, 0.2f //3 }; // 定義紋理貼圖的坐標數據 private float[] taperTextures = {0.5000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f,0.0000f, 1.0000f, 1.0000f, 1.0000f, 0.5000f, 0.0000f,0.0000f, 1.0000f, 1.0000f, 1.0000f, 0.5000f, 0.0000f,1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.5000f, 0.0000f, }; private Context context; private FloatBuffer taperVerticesBuffer; private ByteBuffer[] taperFacts_indices; private FloatBuffer taperTexturesBuffer; // 定義本程序所使用的紋理 private int[] textures = new int[4]; // 旋轉角度 float rotate = 0;定義紋理坐標數據要注意:Android使用的OpenGL ES的紋理坐標系跟官方的OpenGL紋理坐標系統不一樣。官方的OpenGL ES紋理坐標為左下角是(0, 0)右上角是(1, 1);而Android的紋理坐標左上角為(0, 0)右下角為(1, 1)。
3.實現MyRenderer(Context main)方法
public MyRenderer(Context main) {this.context = main;// 將三棱椎的頂點位置數據數組包裝成FloatBuffertaperVerticesBuffer = floatBufferUtil(taperVertices);// 將三棱椎的4個面的數組indices[1:4]包裝成ByteBuffer并加入一個數組中ByteBuffer indices1 = ByteBuffer.wrap(new byte[]{0, 1, 2,0, 0, 0,0, 0, 0,0, 0, 0});ByteBuffer indices2 = ByteBuffer.wrap(new byte[]{0, 0, 0,0, 1, 3,0, 0, 0,0, 0, 0});ByteBuffer indices3 = ByteBuffer.wrap(new byte[]{0, 0, 0,0, 0, 0,1, 2, 3,0, 0, 0});ByteBuffer indices4 = ByteBuffer.wrap(new byte[]{0, 0, 0,0, 0, 0,0, 0, 0,0, 2, 3});taperFacts_indices = new ByteBuffer[]{indices1, indices2, indices3, indices4};// 將立方體的紋理貼圖的坐標數據包裝成FloatBuffertaperTexturesBuffer = floatBufferUtil(taperTextures); }4.紋理映射功能默認是關閉的,所以需要在onSurfaceCreated內啟用紋理映射功能。
// 啟用2D紋理貼圖 gl.glEnable(GL10.GL_TEXTURE_2D); // 裝載紋理 loadTexture(gl);loadTexture()是自定義的一個裝載紋理的方法,實現方法如下:
private void loadTexture(GL10 gl) {Bitmap[] bitmap = new Bitmap[4];try {// 加載位圖bitmap[0] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img1);bitmap[1] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img2);bitmap[2] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img3);bitmap[3] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img4);// 創建紋理對象,指定生成N個紋理(第一個參數指定生成幾個紋理)// textures數組將負責存儲所有紋理的代號gl.glGenTextures(4, textures, 0);for (int i = 0; i < textures.length; i++) {// 通知OpenGL將texture紋理綁定到GL10.GL_TEXTURE_2D目標中gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);// 設置紋理被縮小(距離視點很遠時被縮小)時的濾波方式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[i], 0);}} finally {// 生成紋理之后,回收位圖for (int i = 0; i < bitmap.length; i++) bitmap[i].recycle();} }其中幾個函數的含義如下:
void glGenTextures(int n,int[] textures,int offset)? //創建紋理對象
- n:需要生成的紋理對象個數;
- textures:保存生成所有紋理對象的數組;
- offset:紋理對象數組的偏移(生成的紋理從數組什么位置開始賦值)。
void glBindTexture (int target, int texture)? //綁定紋理,綁定后的紋理才處于活動狀態
- target:紋理類型,使用 2D 紋理則參數設為GL_TEXTURE_2D;
- texture:紋理對象的 ID。?
void glTexParameterf(int target,int pname,float param)? //對綁定的紋理進行基本屬性設置
- target:紋理類型,表示我們激活的紋理單元對應了什么樣的操作類型,比如GL_TEXTURE_1D、GL_TEXTURE_2D等;
- pname:屬性名稱;
- param:屬性值。
void texImage2D (int target, int level, Bitmap bitmap, int border)? //指定紋理
- target:操作的目標類型,本程序均設為 GL_TEXTURE_2D 即可
- level:紋理的級別,表示多級分辨率的紋理圖象的級數。若只有一種分辨率,level為0。
- bitmap:使用的圖像
- border:邊框,一般設為0
Bitmap decodeResource(Resources res, int id)? //加載位圖
- res:包含圖像數據的Resources對象
- id:圖像數據的資源id
?5.繪制圖像及紋理
//繪制圖形的方法 @Override 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(rotate, -0.1f, -0.1f, 0.05f); // 旋轉總坐標系// 設置頂點的位置數據gl.glVertexPointer(3, GL10.GL_FLOAT, 0, taperVerticesBuffer);// 設置貼圖的坐標數據gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, taperTexturesBuffer); // ②//使用for循環給三棱錐每個面都貼上相應的紋理圖for(int i = 0; i < taperFacts_indices.length; i++) {// 綁定紋理,執行紋理貼圖gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);// 按taperFacts_indices[i]指定的面繪制三角形gl.glDrawElements(GL10.GL_TRIANGLES, 3*(i+1), GL10.GL_UNSIGNED_BYTE, taperFacts_indices[i]);}// 繪制結束gl.glFinish();// 禁用頂點、紋理坐標數組gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);//角度+1使繪制的圖形旋轉顯示其他面rotate += 1; }void glTexCoordPointer(int size, int type, int stride, Buffer pointer)? //設置頂點數組為紋理坐標緩存
- size:紋理頂點坐標的分量個數,本程序使用紋理坐標的2個分量(s, t)映射頂點坐標的3個分量(x, y, z);
- type:紋理坐標的數據類型,short, int, float, double都可以;
- stride:位圖的寬度,可以理解為相鄰的兩個紋理之間跨多少個字節,一般為0,因為一般不會在紋理中再添加其他的信息;
- pointer:存放紋理坐標的數組。
?總結:如果想多面體各個面的紋理不一樣,則在畫每個面之前要加上void glBindTexture (int target, int texture)函數綁定紋理。
總結
以上是生活随笔為你收集整理的Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单片机的电子电路基本知识
- 下一篇: HAC集群修改管理员用户密码