3 カメラ
この章では、JavaFX 3Dグラフィックス機能に含まれるCamera APIについて説明します。
カメラは、JavaFX Sceneグラフに追加できるノードとなったため、3D UIレイアウト内で移動できるようになりました。この点は、カメラが1つの位置にとどまっていた2Dレイアウトとは異なります。
JavaFXシーン座標空間では、デフォルトのカメラの投影面はZ=0に配置され、カメラの座標系は次のとおりです。
• X軸は右方向を指します。
• Y軸は下方向を指します。
• Z軸は見る人から反対側の、画面の奥方向を指します。
透視投影カメラ
JavaFXでは、3Dシーンをレンダリングするために透視投影カメラが提供されます。このカメラによって、透視投影の視野空間が定義されます。視野空間は、fieldOfView
プロパティの値を変更することによって変更できます。
例3-1は、透視投影カメラを作成するための2つのコンストラクタを示しています。
後のコンストラクタはJavaFX 8の新しいコンストラクタであり、3D環境でカメラが捉えたものをレンダリングするように、fixedEyeAtCameraZero
フラグを指定してカメラ位置を制御できます。
3Dグラフィックスのプログラミングには、次のコンストラクタを使用する必要があります。
PerspectiveCamera(true);
オプションfixedEyeAtCameraZero
をtrue
に設定すると、投影領域の寸法の変更やウィンドウのサイズ変更にかかわらず、PerspectiveCameraは、眼の位置をその座標空間の(0, 0, 0)に固定して構築されます。
fixedEyeAtCameraZero
をデフォルト値のfalse
に設定すると、カメラによって定義される座標系の原点は、パネルの左上隅になります。このモードは、透視投影カメラでレンダリングされる2D UIコントロールに使用されますが、ほとんどの3Dグラフィックス・アプリケーションには有用ではありません。たとえば、ウィンドウのサイズが変更されると、パネルの左上隅にある原点を保持するためにカメラは移動します。これは2D UIレイアウトの場合に必要ですが、3Dレイアウトでは望ましくありません。そのため、3Dグラフィックスを実行してカメラを変換または移動するときは、fixedEyeAtCameraZero
プロパティを必ずtrue
に設定することが重要です。
カメラを作成してシーンに追加するには、次のコード行を使用します。
Camera camera = new PerspectiveCamera(true); scene.setCamera(camera);
次のコードを使用して、カメラをシーン・グラフに追加します。
Group cameraGroup = new Group(); cameraGroup.getChildren().add(camera); root.getChildren().add(cameraGroup);
カメラを回転させてcameraGroupを移動するには、次のコード行を使用します。
camera.rotate(45); cameraGroup.setTranslateZ(-75);
視野
カメラの視野は、次のように設定できます。
camera.setFieldOfView(double value);
視野が大きくなるほど、透視図のゆがみやサイズの差異が増します。
-
Fisheye
レンズは180度またはそれ以上の最大視野を持ちます。 -
Normal
レンズは40度から62度の視野を持ちます。 -
Telephoto
レンズは1度(未満)から30度の視野を持ちます。
クリップ面
ローカル座標系でのカメラの近クリップ面を次のように設定できます。
camera.setNearClip(double value);
ローカル座標系でのカメラの遠クリップ面を設定するには、次を使用します。
camera.setFarClip(double value);
近クリップ面または遠クリップ面を設定すると、視野空間が決まります。近クリップ面が大きすぎる場合、基本的にはシーンの前方のクリッピングが開始されます。小さすぎる場合は、シーンの背後のクリッピングが開始されます。
ヒント:
不適切なビジュアル・アーティファクトが表れ始める場合があるため、近クリップ値を必要以上に小さい値に設定したり、遠クリップ値を必要以上に大きい値に設定しないでください。
クリップ面は、シーンの十分な部分が表示されるように設定する必要があります。しかし、表示範囲は数値エラーが発生するほど大きい値に設定しないでください。近クリップ面の値が大きすぎると、シーンのクリップが始まります。しかし、近クリップ面が小さすぎると、値がゼロに近すぎることにより、異なる種類のビジュアル・アーティファクトが表示されます。遠クリップ面の値が大きすぎる場合も、特に近クリップ面の値が小さすぎる場合に、数値エラーが発生します。
Y-downとY-up
ほとんどの2Dグラフィックス座標系(UIを含む)では、画面を下方向に進むとYが大きくなります。これは、PhotoShop、JavaFXおよびIllustratorに当てはまります。基本的に、ほとんどの2Dパッケージはこのように機能します。多くの3Dグラフィックス座標系では、通常、画面を上方向に移動するとYが大きくなります。一部の3Dグラフィックス座標系では上方向に移動するとZが大きくなりますが、ほとんどは画面を上方向に移動するとYが大きくなります。
Y-downとY-upは、どちらもそれらの固有のコンテキストでは適切です。JavaFXでは、カメラの座標系はY-downであり、これは、X軸は右方向を指し、Y軸は下方向を指し、Z軸は見る人から反対側の画面の奥方向を指すことを意味します。
Y-upとしての3Dシーンを検討する場合、例3-2に示すように、root3D
というXform
ノードをルートの下に作成できます。そのrx.setAngle
プロパティを180度に設定して、基本的に上下逆にします。次に、3D要素をroot3D
ノードに追加し、カメラをroot3D
の下に置きます。
例3-2 Xformノード(root3D)の作成
root3D = new Xform(); root3D.rx.setAngle(180.0); root.getChildren().add(root3D); root3D.getChildren().add(...); // add all your 3D nodes here
例3-3に示すように、cameraXform
というXformノードを作成し、ルートの下に置くこともできます。それを上下逆にして、カメラをcameraXform
の下に置きます。
例3-3 cameraXformノードの作成
Camera camera = new PerspectiveCamera(true); Xform cameraXform = new Xform(); root.getChildren().add(cameraXform); cameraXform.getChildren().add(camera); cameraXform.rz.setAngle(180.0);
カメラ・ノード上でのわずかな違いがありますが、さらによい方法は、カメラに180度回転を追加することです。自動ピボットを避けるため、自動的に提供される回転は使用しません。例3-4では、カメラは180度回転され、cameraXformの子としてカメラに追加されます。わずかな違いとは、cameraXformは初期の値を保持し、そのデフォルトの位置を保ち、平行移動や回転を含め、すべてのものがゼロになることです。
PerspectiveCameraを使用するサンプル・コード
例3-5に示すSimple3DBoxApp
のサンプルでは、3Dボックスが作成され、シーンのレンダリングに透視投影カメラが使用されます。このサンプル・アプリケーションは、http://www.oracle.com/technetwork/java/javase/downloads/
で「JavaFX Demos and Samples」セクションからダウンロードできるEnsemble 8サンプルの一部です。
MSAAApp.javaアプリケーションでも、Camera
APIの使用方法の例が提供されます。
例3-5 3Dボックスのサンプル・アプリケーション
package simple3dbox; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Parent; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; import javafx.scene.SubScene; import javafx.scene.paint.Color; import javafx.scene.paint.PhongMaterial; import javafx.scene.shape.Box; import javafx.scene.shape.DrawMode; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; import javafx.stage.Stage; public class Simple3DBoxApp extends Application { public Parent createContent() throws Exception { // Box Box testBox = new Box(5, 5, 5); testBox.setMaterial(new PhongMaterial(Color.RED)); testBox.setDrawMode(DrawMode.LINE); // Create and position camera PerspectiveCamera camera = new PerspectiveCamera(true); camera.getTransforms().addAll ( new Rotate(-20, Rotate.Y_AXIS), new Rotate(-20, Rotate.X_AXIS), new Translate(0, 0, -15)); // Build the Scene Graph Group root = new Group(); root.getChildren().add(camera); root.getChildren().add(testBox); // Use a SubScene SubScene subScene = new SubScene(root, 300,300); subScene.setFill(Color.ALICEBLUE); subScene.setCamera(camera); Group group = new Group(); group.getChildren().add(subScene); return group; } @Override public void start(Stage primaryStage) throws Exception { primaryStage.setResizable(false); Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); } /** * Java main for when running without JavaFX launcher */ public static void main(String[] args) { launch(args); } }