יום שני, 12 באוגוסט 2013

Android OpenGL Texture





אחרי שלמדנו לטעון אובייקטים למנוע OpenGL במכשיר, השלב הבא הוא להכניס צבע למודלים אבל לפני שנקפוץ למים העמוקים נתחיל עם אובייקט שנראה פשוט כמו קובייה כבר ראינו שגם קובייה זה אובייקט מורכב במיוחד ב OpenGL אז כדאי להכיר אותו לפני שהולכים לאובייקטים מסובכים יותר.

לקובייה יש 6 משטחים שמורכבים מ 12 משולשים כבר דיברנו על זה במאמר הפתיחה, חשוב לחזור על זה כי בנוסף לנקודות ה Vertices נוספות  נקודות חדשות עבור הטקסטורה.



על מנת להבין את הרעיון יש להסתכל על כל משטח באופן עצמי לדוגמה ניקח את המשטח הראשון שאנחנו מציירים ונוסיף לנו נקודות ציון בסדר שאנחנו רוצים שהוא יפרוס את הטקסטורה על גבי המשטח בעזרת מערך ו Buffer שנוסיף לתהליך.

private float texture[] = {
// Mapping coordinates for the vertices
//FaceA
0.0f, 1.0f,//top left
1.0f, 1.0f,//top right
0.0f,0.0f, //bottom left
1.0f, 0.0f,//bottom right
          }


כפי שניתן לראות אנחנו מציבים במערך Texture את הנקודה השמאלית עליונה ואח"כ את הימנית העליונה ובעצם גורמים לטקסטורה להיות מצוירת מלמעלה ללמטה , אם נשנה את הסדר ונטען את הנקודה השמאלית העליונה עם השמאלית התחתונה נצייר את הטקסטורה משמאל לימין.

private float texture[] = {
// Mapping coordinates for the vertices
//FaceA
0.0f, 1.0f,//top left
0.0f,0.0f, //bottom left
1.0f, 1.0f,//top right
1.0f, 0.0f,//bottom right
        }



דוגמה נוספת למה קורה כאשר מציירים את הנקודות התחתונות ואח"כ את הנקודות העליונות.

private float texture[] = {
// Mapping coordinates for the vertices
//FaceA
0.0f,0.0f, //bottom left
1.0f, 0.0f,//bottom right
0.0f, 1.0f,//top left
1.0f, 1.0f,//top right
  }

את השאר תנחשו לבד


אפשר לצייר את הטקסטורה על המשטח עם כיוון השעון או נגד הכל תלוי בסדר הנקודות שנכנס ל Buffer עבור כל משטח כפי שניתן לראות במחלקה CubeTextue.



CubeTexture

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;

public class CubeTexture {

private GL10 gl;
private Context ctx;
private FloatBuffer mVertexBuffer;
private FloatBuffer mTextureBuffer;
private ByteBuffer mIndexBuffer;
private int texturesPointers[];
private Bitmap texturesMaps[];
private float mCubeRotation;
private float vertices[] = {
// Vertices according to faces
//X     Y      Z
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f,1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f,1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f,-1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f,-1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f,1.0f, -1.0f, 
};

private float texture[] = {
// Mapping coordinates for the vertices
//FaceA
0.0f, 1.0f,//top left
1.0f, 1.0f,//top right
0.0f,0.0f, //bottom left
1.0f, 0.0f,//bottom right
//Face B
0.0f, 1.0f,
1.0f, 1.0f,
0.0f,0.0f,
1.0f, 0.0f,
//Face C
0.0f, 1.0f,
1.0f, 1.0f,
0.0f,0.0f,
1.0f, 0.0f,
//Face D
0.0f, 1.0f,
1.0f, 1.0f,
0.0f,0.0f,
1.0f, 0.0f,
//Face E
0.0f, 1.0f,
1.0f, 1.0f,
0.0f,0.0f,
1.0f, 0.0f,
//Face F
0.0f, 1.0f,
1.0f, 1.0f,
0.0f,0.0f,
1.0f, 0.0f,
};

private byte indices[] = {
// Faces definition
0, 1, 3, 0, 3, 2, 4, 5, 7, 4, 7, 6, 8, 9, 11, 8, 11, 10, 12, 13,
15, 12, 15, 14, 16, 17, 19, 16, 19, 18, 20, 21, 23, 20, 23, 22, };
public CubeTexture(Context _ctx, GL10 _gl)
{
ctx = _ctx;
gl = _gl;
init();
}
//load bitmaps into openGL
private void loadTextures()
{
//for each face of the cube
texturesMaps = new Bitmap[6];
try {
//using the assets folder
texturesMaps[0] = BitmapFactory.decodeStream(ctx.getAssets().open("sideA.jpg"));
texturesMaps[1] = BitmapFactory.decodeStream(ctx.getAssets().open("sideB.jpg"));
texturesMaps[2] = BitmapFactory.decodeStream(ctx.getAssets().open("sideC.jpg"));
texturesMaps[3] = BitmapFactory.decodeStream(ctx.getAssets().open("sideD.jpg"));
texturesMaps[4] = BitmapFactory.decodeStream(ctx.getAssets().open("sideE.jpg"));
texturesMaps[5] = BitmapFactory.decodeStream(ctx.getAssets().open("sideF.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
//create pointers to texture buffer
texturesPointers = new int[texturesMaps.length];
//generate the texture in the opengl
GLES20.glGenTextures(texturesPointers.length, texturesPointers, 0);

for (int i = 0; i < texturesMaps.length; i++) {
gl.glBindTexture(GL10.GL_TEXTURE_2D, texturesPointers[i]);

//texture behavior 
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_LINEAR);
gl.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);

//insert the bitmap into openGL buffer
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, texturesMaps[i], 0);
//no need anymore
texturesMaps[i].recycle();
}
}
private void init()
{
// buffer for vertex coordinates
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
mVertexBuffer = byteBuf.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);

  // buffer for textures coordinates
byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
mTextureBuffer = byteBuf.asFloatBuffer();
mTextureBuffer.put(texture);
mTextureBuffer.position(0);

   // buffer for faces
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);

loadTextures();

}
public void Draw()
{
gl.glLoadIdentity();
gl.glTranslatef(0.1f, 0.0f, -10.0f);
gl.glRotatef(mCubeRotation, 1.0f, 1.0f, 1.0f);
gl.glFrontFace(GL10.GL_CW);

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glEnable(GL10.GL_TEXTURE_2D);

gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);

//texture for each face of the cube
for (int i = 0; i < 6; ++i) {

if (texturesPointers[i] != 0) {
gl.glBindTexture(GL10.GL_TEXTURE_2D, texturesPointers[i]);
}
mIndexBuffer.position(6 * i); 
gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_BYTE,
mIndexBuffer);

}

gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D);

mCubeRotation -= -0.25f;
}
}

טוענים 6 תמונות מתיקיית ה Assets לטיפוסים של Bitmap, מייצרים Buffer בזיכרון של ה OpenGL ובעזרת מערך של  Pointers מצביעים לטקסטורות, מכניסים את ה Bitmap ל Buffer ומשחררים את  ה Bitmap מהזיכרון, בשלב ה Draw מחלקים את המשטחים וטוענים לכל משטח את הטקסטורה מהזיכרון שהגדרנו.

להורדת התוכנית:


סיכום:

אז כמו תמיד מתחילים מהדברים הפשוטים בשלב הבא נטען את נתוני הטקסטורה מקובץ ה OBJ ונשכלל את ה ObjectParser מהמאמר הקודם.

בהצלחה...

אין תגובות:

הוסף רשומת תגובה