前の記事でも書いた通り、JavaFX 8では3Dが正式に追加されJavaFX単体で3Dを利用したアプリケーションが作られるようになりました。しかし、JavaFX 3Dについて日本語で書かれた資料はそこまで多くないというのが現状です。ならば、自分でそういった資料を作って発表していこうと思ったのでJavaFX 3Dについて解説する記事をしばらくは書いていきたいと思います。

JavaFXのアーキテクチャ

JavaFX 3Dの話の前に、簡単にJavaFXのアーキテクチャについて説明します。詳細な説明についてはOracleによる解説文書を参照してもらうとして、この記事では簡単な説明に留めます。

JavaFXアーキテクチャのダイアグラム JavaFXアーキテクチャのダイアグラム

上図の通り、根幹部分にJVMがあり、その上にJDK API群やツール群が存在します。そして、その上にグラフィックスレンダリングに関わる部分(Java 2D、Direct3D、OpenGL)が存在し、それらはPrismエンジンによって利用されます。Prismと同じレイヤにウィンドウ管理やタイマーなどを担当するGlass Windowing Toolkit、画像や音声などメディアを担当するMedia Engine、そしてWebkitベースのWebエンジンコンポーネントであるWeb Engineが存在しています。そしてその上にあるQuantum ToolkitによってPrismやGlass Windowing ToolkitがJavaFX上で利用可能なようにまとめられています。そして最も上にあるのがこれらのツールキットによって構成されるScene Graphなどを担当するJavaFXのAPI群となります。 ここで重要なのは、PrismエンジンやQuantum Toolkitによって2D/3Dグラフィックスの処理がひとまとめになっているという点です。このためか、一般的な3DアプリケーションではY軸が上方向に正であるのに対してJavaFX 3Dでは下方向に正となっており、いわゆる左手座標系をX軸に対して180度回転させたような状態となっています。とりあえずこの座標系については今後JavaFX座標系と呼ぶことにします。

左手座標系 左手座標系
右手座標系 右手座標系
JavaFX座標系 JavaFX座標系

3D図形を表示する

それでは実際にJavaFX 3Dで3D図形を表示させてみます。

FX3DDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.shape.Sphere;
import javafx.stage.Stage;
 
public class FX3DDemo extends Application {
    @Override
    public void start(Stage stage) {
        Group root = new Group();
 
        //半径20の球を作成
        Sphere sphere = new Sphere(20);
        root.getChildren().add(sphere);
 
        //深度バッファとアンチエイリアスを有効にしてSceneを作成
        Scene scene = new Scene(root, 320, 320, true, SceneAntialiasing.BALANCED);
 
        //遠近投影カメラを作成
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setTranslateZ(-100);
        scene.setCamera(camera);
 
        stage.setTitle("JavaFX 3D Demo");
        stage.setScene(scene);
        stage.show();
    }
 
    public static void main(String[] args) {
        launch();
    }
}

このコードをコンパイルし実行すると以下のような結果が得られるはずです。

実行結果 実行結果

画面中央に球が表示され、エッジ部分にはアンチエイリアスが掛かっています。

コード解説

それではコードの主要な部分について解説していきます。Sceneにセットするルートノードとして今回はGroupを利用します。

1
Group root = new Group();

次にShape3DクラスのサブクラスであるSphereクラスのインスタンスを生成し、ルートノードに追加します。

1
2
Sphere sphere = new Sphere(20);
root.getChildren().add(sphere);

そしてSceneのインスタンスを生成しているのですが、ここでは3Dグラフィックスを扱うため、第4・第5引数を取るコンストラクタを使用します。

1
Scene scene = new Scene(root, 320, 320, true, SceneAntialiasing.BALANCED);

第1引数はルートノード、第2・第3引数は幅と高さで3Dを利用しないJavaFXアプリケーションを作成するときと同様ですが、3Dを用いる場合は深度バッファの有効・無効を設定する第4引数とエッジのアンチエイリアスについて設定する第5引数を設定する方が良いです。複雑なモデルを表示する場合に深度バッファが無効だと表示が崩れる場合があるからです。アンチエイリアスについては表示するモデルなどに応じて適切なものを選ぶとよいでしょう。
Sceneを作成したら次にカメラを作成します。カメラには遠近法の要素を含むPerspectiveCameraとパースを行わないParallelCameraの二種類がありますが、今回はPerspectiveCameraを利用しています。

1
2
3
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-100);
scene.setCamera(camera);

PerspectiveCameraには引数を取らないデフォルトコンストラクタとboolean値(fixedEyeAtCameraZero)を取るコンストラクタがありますが、後者では視点をカメラの持つローカル座標系における原点に設定するかどうかを設定できます。デフォルトコンストラクタでカメラを作成した場合(fixedEyeAtCameraZero = false)、カメラの持つローカル座標系における原点でなくパネルの左上が原点として扱われるようになります。つまり、fixedEyeAtCameraZero = tureならば3D向けに、falseなら2D向けの視点となります。3D空間上でカメラを動かしたい場合などはカメラの移動が直観的に行いやすくなるのでtrueにしておく方がよいです。

fixedEyeAtCameraZero = falseの場合 fixedEyeAtCameraZero = falseの場合

なお、球全体を収めるためにsetTranslateZメソッドでカメラの位置を手前側に調節しています。

最後は通常通りにタイトルの設定やStageへのSceneの追加を行い、showメソッドでStageを表示させます。

まとめ

このように、簡単な3Dアプリケーションの作成はカメラの設定とSceneでの深度バッファの設定以外は2Dアプリケーションを作るときとほとんど変わりがありません。しかし、より本格的なものを作ろうとすると光源やマテリアル、SubSceneなどの要素を利用する必要があります。次回以降これらの要素について説明していきたいと思います。