2017年11月6日 星期一

JavaFX 3D - Box

繼上一篇JavaFX 3D System Requirements,本篇將介紹Box類別。

JavaFX 8新增Shape3D抽象類別,繼承自Shape3D抽象類別的3D物件包括Box, Cylinder, MeshView, SphereShape3D抽象類別定義以下屬性
  • cullFace:沿用自OpenGL的GL_CULL_FACE。多邊體 (Polygon) 是由數個面 (Facet) 所組成,每一個面數個頂點 (Vertex) 所設定,在描繪3D物件時,面對鏡頭的一面稱為Front Face,背對鏡頭的一面稱為Back Face由於描繪3D物件需要複雜的計算,因此為增加執行效率,通常會省略描繪背對鏡頭的一面,此功能稱為Cull Face。在OpenGL中,以glCullFace()方法設定省略描繪Front Face或Back Face,欲啟用Cull Face功能,則以glEnable(GL_CULL_FACE)方法設定JavaFX 8的Shape3D抽象類別沿用OpenGL此一功能,以cullFace屬性設定是否省略描繪Front Face或Back Face,分別為CullFace.FRONTCullFace.BACKCullFace.NONE,代表省略描繪Front Face、Back Face或不省略
  • drawMode:定義3D物件的繪製方式,分別為DrawMode.FILLDrawMode.LINE,代表填滿3D物件表面或以Wireframe方式呈現。
  • material定義3D物件表面的材質。
Shape3D抽象類別提供以下方法:
  • getCullFace():取得省略描繪的一面,回傳CullFace.FRONTCullFace.BACKCullFace.NONE,代表省略描繪Front Face、Back Face或不省略。
  • setCullFace()設定省略描繪的一面,預設為CullFace.BACK
  • getDrawMode():取得3D物件的繪製方式,回傳DrawMode.FILLDrawMode.LINE,代表填滿3D物件表面或以Wireframe方式呈現。
  • setDrawMode():設定3D物件的繪製方式,預設為DrawMode.FILL
  • getMaterial()取得3D物件表面的材質。
  • setMaterial()設定3D物件表面的材質。
Box類別用以建立3D方塊,其建構函式如下,分別由widthheightdepth寬度、高與深度

  public Box()
  public Box(double width, double height, double depth)

提供以下方法:
  • getDepth():取得方塊的深度
  • setDepth()設定方塊的深度
  • getHeight():取得方塊的高度
  • setHeight()設定方塊的高度
  • getWidth():取得方塊的寬度
  • setWidth()設定方塊的寬度
請參考以下範例分別示範以setCullFace()setDrawMode()方法不同的組合所產生的效果
// 建立Box物件
Box box = new Box(100, 100, 100); 
// 以Wireframe方式呈現
box.setDrawMode(DrawMode.LINE); 
// 不省略
box.setCullFace(CullFace.NONE); 
// 平移Box物件
box.setLayoutX(150);
box.setLayoutY(150); 
// 旋轉Box物件
box.getTransforms().add(new Rotate(30,Rotate.X_AXIS));
box.getTransforms().add(new Rotate(30,Rotate.Y_AXIS));
box.getTransforms().add(new Rotate(30,Rotate.Z_AXIS)); 

Group root = new Group(); 
root.getChildren().add(box);
...
【執行結果】
// 建立Box物件
Box box = new Box(100, 100, 100); 
// 以Wireframe方式呈現
box.setDrawMode(DrawMode.LINE); 
// 省略Back Face
box.setCullFace(CullFace.BACK); 
...
【執行結果】
// 建立Box物件
Box box = new Box(100, 100, 100); 
// 以Wireframe方式呈現
box.setDrawMode(DrawMode.LINE); 
// 省略Front Face
box.setCullFace(CullFace.FRONT); 
...
【執行結果】
// 建立Box物件
Box box = new Box(100, 100, 100); 
// 以填滿方式呈現
box.setDrawMode(DrawMode.FILL); 
// 省略Back Face
box.setCullFace(CullFace.BACK); 
...
【執行結果】
// 建立Box物件
Box box = new Box(100, 100, 100); 
// 以填滿方式呈現
box.setDrawMode(DrawMode.FILL); 
// 省略Front Face
box.setCullFace(CullFace.FRONT); 
...
【執行結果】
以下範例示範setMaterial()方法設定物件表面的材質,其中以PhongMaterial類別設定材質的相關顏色 (註)
// 建立Box物件
Box box = new Box(100, 100, 100); 
// 以填滿方式呈現
box.setDrawMode(DrawMode.FILL); 
// 設定材質
PhongMaterial material = new PhongMaterial(); 
// Diffuse Color 
material.setDiffuseColor(Color.YELLOW); 
// Specular Color 
material.setSpecularColor(Color.WHITE); 
// 設定物件表面的材質
box.setMaterial(material);
// 平移Box物件
box.setLayoutX(150);
box.setLayoutY(150); 
// 旋轉Box物件
box.getTransforms().add(new Rotate(30,Rotate.X_AXIS));
box.getTransforms().add(new Rotate(30,Rotate.Y_AXIS));
box.getTransforms().add(new Rotate(30,Rotate.Z_AXIS)); 

Group root = new Group(); 
root.getChildren().add(box);
...
【執行結果】
以下範例示範setMaterial()方法設定貼圖
// 建立Box物件
Box box = new Box(100, 100, 100); 

Image image = new Image(  
  getClass().getResourceAsStream("images/Javafx.png")); 

// 設定材質
PhongMaterial material = new PhongMaterial(); 
// 設定貼圖
material.setDiffuseMap(image);
...
【執行結果】
此外,可加入滑鼠事件,當滑鼠移動時 (Mouse Moved),藉由Rotate類別旋轉物件
scene.setOnMouseMoved((MouseEvent e) -> {
  newX = e.getX(); 
  if (Math.abs(newX - oldX) > 5) { 
    box.getTransforms().add(new Rotate(newX - oldX, Rotate.X_AXIS)); 
    oldX = newX; 
  } 

  newY = e.getY(); 
  if (Math.abs(newY - oldY) > 5) { 
    box.getTransforms().add(new Rotate(newY - oldY, Rotate.Y_AXIS)); 
    oldY = newY; 
  } 
});
...
【執行結果】
亦可試著以Timeline類別設定時間軸,以產生動畫效果
final Timeline timeline = new Timeline(
  new KeyFrame(Duration.millis(50), new EventHandler() { 

  double deltaX = 20.0, deltaY = 30.0, deltaZ = 10.0; 

  @Override public void handle(final ActionEvent e) {  
    box.getTransforms().add(new Rotate(deltaX, Rotate.X_AXIS)); 
    deltaX += 10; 
    box.getTransforms().add(new Rotate(deltaY, Rotate.Y_AXIS)); 
    deltaY += 10; 
    box.getTransforms().add(new Rotate(deltaZ, Rotate.Z_AXIS)); 
    deltaZ += 10; 
  } 
})); 

timeline.setCycleCount(Timeline.INDEFINITE); 
timeline.play();
...
(註)

JavaFX使用Phong Shading著色法,又稱為Phong Interpolation或Normal-Vector Interpolation Shading,是由美國越南裔學者Bui T??ng Phong所發明,1973年發表於博士論文 "Illumination of Computer-Generated Images", Department of Computer Science, University of Utah, UTEC-CSs-73-129, July 1973,因此以其姓氏Phong (裴)命名。 

Phong Reflection Model是由Ambient Light (環境光)、Diffuse Light (漫射光) 與Specular Light (鏡面光) 三者所組成。 

JavaFX的Phong Material細分為Bump Map, Diffuse Map, Self Illumination Map, Specular Map, Diffuse Color, Specular Color, 與Specular Power,前四者Map用以設定貼圖、接著Color用以設定著色、最後Power用以設定強度。

【參考資料】

[1] 黃嘉輝,深入研究JavaFX 2。
[2] 黃嘉輝,JavaFX遊戲程式設計。
[3] Java Official Web Site:http://www.oracle.com/technetwork/java/index.html
[4] JavaFX:http://www.oracle.com/technetwork/java/javafx
[5] JavaFX 8.0 API Specification.
[6] Java Platform, Standard Edition 8 API Specification.
[7] JDK 8 Certified System Configurations
[8] H. M. Cundy, A. P. Rollett, Mathematical Models, Tarquin Publications, 1981.
[9] G. Sellers, R. S. Wright, N. Haemel, OpenGL SuperBible: Comprehensive Tutorial and Reference (6th Edition), Addison-Wesley Professional, 2013.

© Chia-Hui Huang

沒有留言:

張貼留言