JavaFXでバンプマッピング
このEntryは JavaFX Advent Calendar の最終日です。今年は誰も落とさずに、Adventしました!!すばらしい!!!!
昨日は @masanori_msl さんの JavaFX + Apache POIでSpreadsheet操作 でした。
さて、バンプマッピングです。
なぜ、バンプマッピングかというと、JavaFX Advent Calendar の 19 日目のひらおかさんの JavaFX で小惑星を描いてみよう で、私が Twitter で次のようにコメントしたからです。
@yumix_h SquareはSphereのtypoでしょうか。また、小惑星の表面はバンプマッピングした方がらしく見えると思いますよ。
— Yuichi Sakuraba (@skrb) 2016年12月19日
とコメントしたのにもかかわらず、バンプマッピングのことってどこかに書いたことないなぁと思い出したわけです。
バンプマッピングとは
バンプ (bump) はデコボコのことです。ようするに、3D のオブジェクトの表面を擬似的にデコボコに描画することをいいます。
あくまでも擬似的なので、ほんとにデコボコを描くわけではないのですが、意外に使えるのです。
ちなみに、ほんとにデコボコを作るマッピングとしては、ディスプレイスメントマッピングというものがありますが、JavaFX ではサポートしていないのです ><
で、バンプマッピング。
バンプマッピングにもいろいろあるらしいのですが、もっとも一般的で JavaFX でもサポートしている法線ベクトルを使用する方法を紹介します。
物体の表面の接線に直交している法線というのものを考えます。高校の数学でも法線出てきているので、思い出してね。
法線は英語だと Normal といいます。
その物体に当たった光は、その法線との入射角と同じ角度で反射します。
表面が均一だと、法線の向きもそろうのでキレイに反射します。鏡の表面のようなものです。でも、表面が荒れていて乱雑だと、法線の向きもバラバラになるので、反射光もどこに向かうか分からなくなります。すると、表面がマッドな感じになるわけです。
一般的には法線はベクトルとして扱います。3D CG をやっているとよく出てくる、法線ベクトルというやつです。
前述したように法線は表面の接面に直交したベクトルなのですが (3D なので接線ではなくて接面です)、これを意図的に変えてみようというのがバンプマッピングの基本的な考えです。
どういうことかというと、表面が均一でも下の図のように法線を違う方向にあえて向けてしまうのです。
すると、人間はあたかもそこにデコボコがあるかのように認知してしまうわけです。上の絵の場合、突起ですが、へっこみも同じように法線を内向きにすれば可能です。
でも、法線ベクトルを変更しているだけなので、本当にデコボコがあるわけではありません。あくまでも擬似的なものです。
バンプマッピングの準備
さて、バンプマッピングをやりたいわけですが、それに先駆けてやらなければならないことがあります。法線ベクトルを先に準備しなければならないということです。
オブジェクトにテクスチャを貼るように、法線ベクトルを表したマップを準備するのです。
マップの各ピクセルが法線ベクトルを表すようにします (厳密にはテクスチャと同じように、UV マッピングするので、ピクセルではないのですが...)。
では、どうやって各ピクセルで法線ベクトルを表すかというと、RGB の値にベクトルの x 座標 y 座標 z 座標を割り当ててしまいます。
といっても、そんなの作れないと思いますよね。実際には、ツールで作ってしまいます。
ここでは Photoshop を使った方法を紹介します。
今回はサンプルとして球に火星のテクスチャを貼る場合を考えてみます。火星の地形図は Wikipedia の GeoTemplate/mars を使用しました。
まずはこのイメージを Photoshop に読み込みます。そして、メニューバーの [フィルタ] - [3D] - [法線マップを生成] を選択します。
すると、下図のようなダイアログが表示されます。これは生成した法線マップでバンプマッピングしたものです。これで、法線マップがちゃんと使えそうかどうかを確認します。
ここでは、球に貼ろうとしているので、そのままデフォルトでかまいません。もし、違う形状に貼ろうとするのであれば、左下の [オブジェクト] を変更します。
他のパラメータは基本的には触らなくても大丈夫です。
これで、[OK] すると、元々の表示が紫主体の表示に変化しているはずです。後はこれを保存するだけです。
昔の赤青フィルタで作った 3D の映画のようですね。
他のツールでも同じように作成できるはずです。フリーのツールもあるので、ググってみてください。私はもっぱら Photoshop を使っているので、他のツールのどれがいいのかよく分からないのですが ^ ^;;
さて、これで法線マップができました。
JavaFX でバンプマッピングをする
準備はできたので、JavaFX でバンプマッピングをしてみましょう。
といってもそんなに難しいことではありません。JavaFX でテクスチャを貼る時に使用する javafx.scene.paint.PhongMaterial クラスを、バンプマッピングでも使用します。
PhongMaterial material = new PhongMaterial(); // テクスチャの設定 material.setDiffuseMap(new Image(テクスチャの画像のURL文字列)); // バンプマッピングの設定 material.setBumpMap(new Image(法線マップ画像のURL文字列)); // オブジェクトにマテリアルを設定 mars.setMaterial(material);
setDiffuseMap メソッドがテクスチャで、setBumpMap メソッドが法線マップです。後はこれを 3D のノードにセットするだけです。
上のコードでは変数 mars が Sphere クラスのオブジェクトになっています。
これでおしまい。準備の方が大変なぐらいですね。
バンプマッピングをしたものと、していないものを比較して見ましょう。
左がバンプマッピング有り、右が無しです。
テクスチャを貼っただけでも何となくデコボコに見えますが、バンプマッピングをするとさらにデコボコが大きくなるような感じです。微妙といえば微妙かもしれませんが...
また、本当にデコボコを作っているわけではないので、縁の部分などはアラが見えてしまいます。それでも、十分使えると思いませんか。
ソースは gist にあげてあります。