2014年2月2日 星期日

JOGL Reflection

反射 (Reflection) 是由於光自物體表面反彈所造成的現象,光反彈的方向受到物體表面的幾何結構影響,非常光滑的表面會產生完全相同的反射方向,就像鏡子一般;若是非常的粗糙表面則會產生散射 (Dispersion),自光滑表面所產生的反射稱為高光反射 (Specular Reflection),自粗糙表面所產生的反射稱為散射反射 (Diffuse Reflection),而介於兩者之間則稱為光滑反射 (Glossy Reflection)。非導電材質的反射總是與光源顏色相同,所以所產生的反射是白色,而導電材質則會讓反射的光線帶有顏色,其顏色由入射角度與材質的化學結構決定。

在JOGL中,反射如同在反射的表面上再繪製物體,使其產生反射的效果。可使用javax.media.opengl.GL類別的glStencilFunc()glStencilOp()方法處理。例如:


gl.glColorMask(false, false, false, false);
gl.glDepthMask(false);

gl.glEnable(GL.GL_STENCIL_TEST);
gl.glStencilFunc(GL.GL_ALWAYS, 1, ~0);
gl.glStencilOp(GL.GL_REPLACE, GL.GL_REPLACE, GL.GL_REPLACE);

drawSurface();

gl.glColorMask(true, true, true, true);
gl.glDepthMask(true);

gl.glStencilFunc(GL.GL_EQUAL, 1, ~0);

gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP);

gl.glPushMatrix();
  gl.glScalef(1.0f, -1.0f, 1.0f);
  gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, g_lightPos, 0);
  drawCube();
gl.glPopMatrix();

gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, g_lightPos, 0);
gl.glEnable(GL.GL_BLEND);
drawSurface();
gl.glDisable(GL.GL_BLEND);

gl.glPushMatrix();
  gl.glDisable(GL.GL_TEXTURE_2D);
  gl.glDisable(GL.GL_LIGHTING);
  gl.glDisable(GL.GL_DEPTH_TEST);
  gl.glEnable(GL.GL_BLEND);
  gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_INCR);
  gl.glColor4f(0.0f, 0.0f, 0.0f, 0.5f);

  gl.glMultMatrixf(g_shadowMatrix, 0);
  drawCube();

  gl.glEnable(GL.GL_TEXTURE_2D);
  gl.glEnable(GL.GL_DEPTH_TEST);
  gl.glDisable(GL.GL_BLEND);
  gl.glEnable(GL.GL_LIGHTING);
gl.glPopMatrix();
gl.glDisable(GL.GL_STENCIL_TEST);

gl.glPushMatrix();
  drawCube();
gl.glPopMatrix();


請參考以下範例。


// 實作GLEventListener介面之方法
// 初始化JOGL
public void init(GLAutoDrawable drawable) {
  gl = drawable.getGL();
  
  // Clear window with color   
  gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

  // set the shading model
  gl.glShadeModel(GL.GL_SMOOTH);

  // set up a single white light
  float lightColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };

  gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, lightColor, 0);
  gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, lightColor, 0);

  gl.glEnable(GL.GL_LIGHTING);
  gl.glEnable(GL.GL_LIGHT0);
  gl.glEnable(GL.GL_DEPTH_TEST);

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

  // load the textures
  cubeTex = loadTexture("opengl.jpg");
  marbleTex = loadTexture("marble.jpg");

  gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

  float[] plane = { 0.0f, 1.0f, 0.0f, 0.0f };

  // dot product of plane and light position
  float dot = plane[0] * g_lightPos[0] + plane[1] * g_lightPos[1] + 
              plane[1] * g_lightPos[2] + plane[3] * g_lightPos[3];

  // first column
  g_shadowMatrix[0]  = dot  - g_lightPos[0] * plane[0];
  g_shadowMatrix[4]  = 0.0f - g_lightPos[0] * plane[1];
  g_shadowMatrix[8]  = 0.0f - g_lightPos[0] * plane[2];
  g_shadowMatrix[12] = 0.0f - g_lightPos[0] * plane[3];

  // second column
  g_shadowMatrix[1]  = 0.0f - g_lightPos[1] * plane[0];
  g_shadowMatrix[5]  =  dot - g_lightPos[1] * plane[1];
  g_shadowMatrix[9]  = 0.0f - g_lightPos[1] * plane[2];
  g_shadowMatrix[13] = 0.0f - g_lightPos[1] * plane[3];

  // third column
  g_shadowMatrix[2]  = 0.0f - g_lightPos[2] * plane[0];
  g_shadowMatrix[6]  = 0.0f - g_lightPos[2] * plane[1];
  g_shadowMatrix[10] =  dot - g_lightPos[2] * plane[2];
  g_shadowMatrix[14] = 0.0f - g_lightPos[2] * plane[3];

  // fourth column
  g_shadowMatrix[3]  = 0.0f - g_lightPos[3] * plane[0];
  g_shadowMatrix[7]  = 0.0f - g_lightPos[3] * plane[1];
  g_shadowMatrix[11] = 0.0f - g_lightPos[3] * plane[2];
  g_shadowMatrix[15] =  dot - g_lightPos[3] * plane[3];    
}

// 載入圖像以作為貼圖之用
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();

  gl.glLoadIdentity();
  glu.gluLookAt(0.0, 3.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

  // Clear
  gl.glClear(GL.GL_COLOR_BUFFER_BIT | 
    GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);

  // rotate the scene
  angle += 3.0;
  gl.glRotated(-angle/8.0, 0.0, 1.0, 0.0);
  gl.glRotated(10.0 * Math.sin(angle/45.0), 1.0, 0.0, 0.0);

  gl.glColorMask(false, false, false, false);
  gl.glDepthMask(false);

  gl.glEnable(GL.GL_STENCIL_TEST);
  gl.glStencilFunc(GL.GL_ALWAYS, 1, ~0);
  gl.glStencilOp(GL.GL_REPLACE, GL.GL_REPLACE, GL.GL_REPLACE);

  drawSurface();

  gl.glColorMask(true, true, true, true);
  gl.glDepthMask(true);
  
  gl.glStencilFunc(GL.GL_EQUAL, 1, ~0);
  
  gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP);

  gl.glPushMatrix();
    gl.glScalef(1.0f, -1.0f, 1.0f);
    gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, g_lightPos, 0);
    drawCube();
  gl.glPopMatrix();

  gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, g_lightPos, 0);
  gl.glEnable(GL.GL_BLEND);
  drawSurface();
  gl.glDisable(GL.GL_BLEND);

  gl.glPushMatrix();
    gl.glDisable(GL.GL_TEXTURE_2D);
    gl.glDisable(GL.GL_LIGHTING);
    gl.glDisable(GL.GL_DEPTH_TEST);
    gl.glEnable(GL.GL_BLEND);
    gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_INCR);
    gl.glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
  
    gl.glMultMatrixf(g_shadowMatrix, 0);
    drawCube();
  
    gl.glEnable(GL.GL_TEXTURE_2D);
    gl.glEnable(GL.GL_DEPTH_TEST);
    gl.glDisable(GL.GL_BLEND);
    gl.glEnable(GL.GL_LIGHTING);
  gl.glPopMatrix();
  gl.glDisable(GL.GL_STENCIL_TEST);

  gl.glPushMatrix();
    drawCube();
  gl.glPopMatrix();
}

private void drawSurface(){
  float[] surfaceColor = { 1.0f, 1.0f, 1.0f, 0.6f };
  gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, surfaceColor, 0);
  gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, 
    surfaceColor, 0);
  gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 200.0f);

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

  gl.glBegin(GL.GL_QUADS);
  gl.glNormal3f(0.0f, 1.0f, 0.0f);

  float x = -5.0f;
  float z = -5.0f;

  for (int i = 0; i < 10; i++, x += 1.0f) {
    for (int j = 0; j < 10; j++, z += 1.0f) {
      gl.glTexCoord2f(0.0f, 0.0f);
      gl.glVertex3f(x, 0.0f, z);
      gl.glTexCoord2f(1.0f, 0.0f);
      gl.glVertex3f(x + 1.0f, 0.0f, z);
      gl.glTexCoord2f(1.0f, 1.0f);
      gl.glVertex3f(x + 1.0f, 0.0f, z + 1.0f);
      gl.glTexCoord2f(0.0f, 1.0f);
      gl.glVertex3f(x, 0.0f, z + 1.0f);
    }
    z = -5.0f;
  }

  gl.glEnd();

  marbleTex.disable();  



【執行結果】


【參考資料】

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


© Chia-Hui Huang

沒有留言:

張貼留言