2014年2月2日 星期日

JOGL Texture

在實心 (Solid) 物體的表面,可藉由貼圖將圖像貼至物體表面,此稱為Texture (紋理)。

圖像為一數位影像,因此貼圖其實是由一訊號取樣後之結果,此過程稱為重新取樣 (Re-sampling)。由於為取樣結果,因此會有兩種情況:一是放大取樣,則取樣點的間距較原先貼圖的取樣點間距小;二是縮小取樣,則取樣點的間距較原先貼圖的取樣點間距大。

常見的取樣方法有線性內插法 (Linear Interpolation)、雙線性內插法 (Bi-linear Interpolation)、三次多項式內插法 (Cubic Interpolation)、雙三次多項式內插法 (Bi-cubic Interpolation) 等。

由於貼圖為取樣結果,因此會產生貼圖前後變質的情況,稱為失真。為解決失真的問題,Lance Williams於1983年7月在Computer Graphics Vol. 17 No.3提出一篇名為"Pyramidal Parametrics"的論文,為失真提出解決方法,若事先將貼圖用各種大小的Filter處理,並建立不同的貼圖,則在貼圖縮小時,只要選擇適當的貼圖大小則可,這種方法稱為MIP Map,其中MIP為拉丁文multum in parvo的縮寫,為many things in a small place或much in little之意。

在JOGL中,以com.sun.opengl.util.texture套件處理Texure,其類別包括:
  • Texture:建立Texure物件。
  • TextureCoords:設定Texure矩形區域的座標,分別為區域的上、下、左、右座標。
  • TextureData:代表Texure的相關資料,如高度、寬度、Pixel格式、MIP Map等。
  • TextureIO:建立輸出入資料流,以自圖像檔案或資料流中載入至Texure物件、或自Texure物件寫入至記憶體資料流中。
以載入圖像為例,可由TextureIO類別的newTexture()方法自檔案或資料流中載入,其圖像格式可為:
  • TextureIO.DDS:Microsoft DirectDraw Surface (DDS) 格式。
  • TextureIO.GIF:Graphics Interchange Format (GIF) 格式。
  • TextureIO.JPG:Joint Photographic Experts Group (JPEG) 格式。
  • TextureIO.PNG:W3C Portable Network Graphics (PNG) 格式。
  • TextureIO.SGI:SGI格式。
  • TextureIO.SGI_RGB:SGI RGB格式。
  • TextureIO.TGA:Targa格式。
  • TextureIO.TIFF:Tag Image File Format (TIFF) 格式。 
並可由Texture類別的setTexParameteri()方法設定其Wrap與Filter的方式。

載入圖像的程式架構:


com.sun.opengl.util.texture.Texture texture = null;

// Get current classloader
ClassLoader cl = this.getClass().getClassLoader();

try {
  // 載入圖像檔案
  texture = TextureIO.newTexture(
    cl.getResourceAsStream(filename), false, TextureIO.JPG);
  // 設定Texture的參數
  texture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
  texture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
  texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
  texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
}
catch(Exception e) {...}


至於貼圖,則依序以Texture類別的enable()bind()disable()方法分別啟用、繫結與停止貼圖,所謂繫結 (Bind) 是將圖像貼至物體表面的過程,此過程依序以javax.media.opengl.GL類別的glNormal3f()glTexCoord2f()glVertex3f()方法設定物體表面的法線 (Normal)、Texture的座標與頂點 (Vertex),藉由法線可決定表面向內或向外的方向性,藉由座標與頂點可繪製物體表面。

例如以下之程式代表貼圖至物體的正面 (Front View):


// Texture貼圖  
Texture cubeTexture;
...

// 啟用貼圖
cubeTexture.enable();
// 繫結貼圖
cubeTexture.bind();

gl.glBegin(GL.GL_QUADS);

  // front
  // 設定物體表面的法線
  gl.glNormal3f(0.0f, 0.0f, 1.0f);
  // 設定Texture的座標與頂點(Vertex)
  gl.glTexCoord2f(0.0f, 0.0f);
  gl.glVertex3f(-30.0f, -30.0f, 30.0f);
  gl.glTexCoord2f(1.0f, 0.0f);
  gl.glVertex3f( 30.0f, -30.0f, 30.0f);
  gl.glTexCoord2f(1.0f, 1.0f);
  gl.glVertex3f( 30.0f,  30.0f, 30.0f);
  gl.glTexCoord2f(0.0f, 1.0f);
  gl.glVertex3f(-30.0f,  30.0f, 30.0f);
  ...

gl.glEnd();
// 停止貼圖
cubeTexture.disable();


請參考以下範例。


// 實作GLEventListener介面之方法
// 初始化JOGL
public void init(GLAutoDrawable drawable) {
  gl = drawable.getGL();

  gl.glShadeModel(GL.GL_SMOOTH);   
  gl.glEnable(GL.GL_DEPTH_TEST);   
  gl.glEnable(GL.GL_CULL_FACE);    
  gl.glFrontFace(GL.GL_CCW);       

  gl.glEnable(GL.GL_LIGHTING);     
  
  // Light 0
  gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, diffuseLight, 0);
  gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, specularLight, 0);
  gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPosition, 0);
  gl.glLightf(GL.GL_LIGHT0, GL.GL_SPOT_CUTOFF, 40.0f);
  gl.glLightf(GL.GL_LIGHT0, GL.GL_SPOT_EXPONENT, 80.0f);

  // Light 1
  gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, diffuseMaterial, 0);
  gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, diffuseMaterial, 0);
  gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightPositionR, 0);

  // 啟用光源 
  gl.glEnable(GL.GL_LIGHT0);
  gl.glEnable(GL.GL_LIGHT1);

  gl.glEnable(GL.GL_COLOR_MATERIAL);
  gl.glColorMaterial(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE);

  // 設定物體的材質
  gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, specularLight, 0);
  gl.glMateriali(GL.GL_FRONT, GL.GL_SHININESS, 128);

  // 以目前之顏色清除視窗
  gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

  // 載入圖像以作為貼圖之用
  cubeTexture = loadTexture("opengl.jpg");
}

// 載入圖像以作為貼圖之用
private Texture loadTexture(String filename) {
  com.sun.opengl.util.texture.Texture tex = null;

  // Get current classloader
  ClassLoader cl = this.getClass().getClassLoader();
  
  try {
    // 載入圖像檔案
    tex = com.sun.opengl.util.texture.TextureIO.newTexture(
      cl.getResourceAsStream("images/" + filename), 
      false, TextureIO.JPG);

    // 設定Texture的參數
    tex.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
    tex.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
    tex.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
    tex.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
  }
  catch(Exception e) { 
    System.out.println("Error loading texture " + filename);  
  }

  return tex;


// 繪製圖形
public void display(GLAutoDrawable drawable) {
  gl = drawable.getGL();

  // increase rotation values
  objectXRot += 0.5f;
  objectYRot += 1.0f;
  objectZRot += 0.5f;
  
  // clear screen and depth buffer
  gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

  // 載入單位矩陣
  gl.glLoadIdentity();                    

  // Move everything back to (0, 0, -150)
  gl.glTranslatef(0.0f, 0.0f, -150.0f);

  // 複製目前之矩陣至堆疊最上層
  gl.glPushMatrix();

  // 設定目前繪圖顏色
  gl.glColor3f(1.0f, 1.0f, 1.0f);
  // 沿x座標旋轉指定角度
  gl.glRotatef(objectXRot, 1.0f, 0.0f, 0.0f);
  // 沿y座標旋轉指定角度
  gl.glRotatef(objectYRot, 0.0f, 1.0f, 0.0f);
  // 沿z座標旋轉指定角度
  gl.glRotatef(objectZRot, 0.0f, 0.0f, 1.0f);

  drawCube();
  
  // 自堆疊最上層移除矩陣
  gl.glPopMatrix();
  // 即刻執行JOGL指令 
  gl.glFlush();

  drawable.swapBuffers(); 
}

// 當視窗大小改變時
public void reshape(GLAutoDrawable drawable, 
  int x, int y, int width, int height) {
  
  GL gl = drawable.getGL();

  if (height == 0)
    height = 1;
    
  // 設定視界大小  
  gl.glViewport(0, 0, width, height);

  // 設定座標系統
  gl.glMatrixMode(GL.GL_PROJECTION);
  // 載入單位矩陣
  gl.glLoadIdentity();

  // 設定透視度
  glu.gluPerspective(54.0, (double)width/(double)height, 1.0, 1000.0);

  // 設定座標系統
  gl.glMatrixMode(GL.GL_MODELVIEW);
  // 載入單位矩陣
  gl.glLoadIdentity();
}

private void drawCube(){
  float[] cubeColor = { 1.0f, 1.0f, 1.0f, 1.0f };
  
  gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, cubeColor, 0);
  gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, cubeColor, 0);
  gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 50.0f);

  gl.glEnable(GL.GL_TEXTURE_2D);
  gl.glTexEnvf(GL.GL_TEXTURE_ENV, 
    GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);

  // 啟用貼圖
  cubeTexture.enable();
  // 繫結貼圖
  cubeTexture.bind();

  gl.glBegin(GL.GL_QUADS);

    // front
    // 設定物體表面的法線
    gl.glNormal3f(0.0f, 0.0f, 1.0f);
    // 設定Texture的座標與頂點(Vertex)
    gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-30.0f, -30.0f, 30.0f);
    gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f( 30.0f, -30.0f, 30.0f);
    gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f( 30.0f,  30.0f, 30.0f);
    gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-30.0f,  30.0f, 30.0f);
  
    // back
    // 設定物體表面的法線
    gl.glNormal3f(0.0f, 0.0f, -1.0f);
    // 設定Texture的座標與頂點(Vertex)
    gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f( 30.0f, -30.0f, -30.0f);
    gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(-30.0f, -30.0f, -30.0f);
    gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(-30.0f,  30.0f, -30.0f);
    gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f( 30.0f,  30.0f, -30.0f);
  
    // top
    // 設定物體表面的法線
    gl.glNormal3f(0.0f, 1.0f, 0.0f);
    // 設定Texture的座標與頂點(Vertex)
    gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-30.0f, 30.0f,  30.0f);
    gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f( 30.0f, 30.0f,  30.0f);
    gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f( 30.0f, 30.0f, -30.0f);
    gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-30.0f, 30.0f, -30.0f);
  
    // bottom
    // 設定物體表面的法線
    gl.glNormal3f(0.0f, -1.0f, 0.0f);
    // 設定Texture的座標與頂點(Vertex)
    gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-30.0f, -30.0f, -30.0f);
    gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f( 30.0f, -30.0f, -30.0f);
    gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f( 30.0f, -30.0f,  30.0f);
    gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-30.0f, -30.0f,  30.0f);
  
    // left
    // 設定物體表面的法線
    gl.glNormal3f(-1.0f, 0.0f, 0.0f);
    // 設定Texture的座標與頂點(Vertex)
    gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-30.0f, -30.0f, -30.0f);
    gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(-30.0f, -30.0f,  30.0f);
    gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(-30.0f,  30.0f,  30.0f);
    gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-30.0f,  30.0f, -30.0f);
  
    // right
    // 設定物體表面的法線
    gl.glNormal3f(1.0f, 0.0f, 0.0f);
    // 設定Texture的座標與頂點(Vertex)
    gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(30.0f, -30.0f,  30.0f);
    gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(30.0f, -30.0f, -30.0f);
    gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(30.0f,  30.0f, -30.0f);
    gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(30.0f,  30.0f,  30.0f);

  gl.glEnd();
  // 停止貼圖
  cubeTexture.disable();  

  gl.glDisable(GL.GL_TEXTURE_2D);
}


【執行結果】


【參考資料】

[1] Java Platform, Standard Edition 7, API Specification.
[2] JOGL.
[3] 黃嘉輝,完全探索Java遊戲程式設計,上奇資訊。

© Chia-Hui Huang

沒有留言:

張貼留言