Java で 3D Graphics Engine を実装するに辺り、算術演算の過程で避けられない一時変数の取り扱いに悩んでいました。
例えば、Matrix と Vector3 クラスを作り、位置とターゲット地点と up ベクトルから姿勢行列を生成する createLookAt メソッドの実装を考えます。
この処理の過程では X/Y/Z 軸の算出が不可欠であり、通常は軸を Vector3 インスタンスで表現したいと考えます。しかし、メソッド内部に閉じた一時変数であるため、GC 負荷の軽減を考慮すると単純な new による Vector3 生成は避けるべきです。
で、どうすりゃいいの?って話です。
jMonkeyEngine (JME) のコードを見ると、そのような一時変数用に TempVars クラスを定義し、そのインスタンスを TheadLocal 変数として管理しているようです。
コレだ!と。
ただまぁ、JME では、ありとあらゆる一時変数を TempVars クラスで一括定義している事に違和感があるため、自分のコードでは純粋に 1 つのクラスのインスタンスのみを管理する MatrixPool や Vector3Pool クラスを定義して利用することにしました。要するに、必要のないクラス間の依存を避けたいってことです。コード量は増えますが、僕は不要な依存を避ける方が重要だと思っています。
後は、Java 7 より try-with-resources 構文が使えるため、プールへのインスタンスの返却を簡素化するために、プールするクラスに Closeable を implements し、close メソッドでプールへ返却する仕組みとしました。
インスタンスのプール管理では、インスタンスのプールへの返却忘れというミスが十分に考えられます。このミスを減らすために try-with-resources を利用するわけです。
まぁ、可読性も向上しているんじゃないなかな・・・と、僕は感じます。
2016/10/26
2016/01/20
最近のお気に入り - Kobito
最近は、何かしらの情報を記載する時には全て Kobito を使っています。
Electron を調べていたのと、仕事で情報共有に使うツールを探していたら Qiita: Team に辿り着き、Kobito にも辿り着いたって感じです。
殆どの情報はテキスト形式で十分であると僕は考えていますが、単なるテキスト エディタで記述する場合、自分自身でレイアウトに迷うことが多く、また、人により構成が異なる所にイライラしていました。このイライラ解消には Markdown が最適 (完全ではなくベスト) であると感じ、Kobito を愛用している所です。
特に、コードの断片を記述する時に便利ですね。様々な言語用に自動的に整形する機能が便利です。従事している仕事の性質上、コードの断片を記述することが非常に多いです。コーディング規約、コード例、導入手順・・・などなど。
作業指示なども、Kobito で Markdown で書いて、相手にも Kobito のインストールを強制し、エクスポートしたテキスト ファイルを渡し、Kobito で開いて確認してね、というノリでやってます。仮に、Kobito を使わずに、一般的なテキスト エディタで見ても問題はないでしょうし。
他にも、打ち合わせでは Kobito で書いた内容をモニタに映して説明しています。
バージョン管理システムで文書も同時に管理する事を考えると、Markdown は差分把握に良い形式です。ゆえに GitHub の README も Markdown なのでしょう。
コピペの際にも便利ですね。テキスト ベースの場合、コピー元の書式にとらわれる事が無いので。変に凝ったツールでは、コピー元の書式までコピーしてペーストしてしまうので、かなりウザいです。
・・・って言うか、書いていて楽しいです。プレビューで整形結果をリアルタイムに見ながら、テキストでサクサクと情報を記述できるので。
欲を言うならば、Qiita 同様に目次も表示してくれると良いのですが・・・。後は、Qiita とは独立して、Kobito 単体でファイルを階層化できればなぁ、と。
Kobito
Electron を調べていたのと、仕事で情報共有に使うツールを探していたら Qiita: Team に辿り着き、Kobito にも辿り着いたって感じです。
Electron
Qiita: Team
殆どの情報はテキスト形式で十分であると僕は考えていますが、単なるテキスト エディタで記述する場合、自分自身でレイアウトに迷うことが多く、また、人により構成が異なる所にイライラしていました。このイライラ解消には Markdown が最適 (完全ではなくベスト) であると感じ、Kobito を愛用している所です。
Markdown - Wikipedia
特に、コードの断片を記述する時に便利ですね。様々な言語用に自動的に整形する機能が便利です。従事している仕事の性質上、コードの断片を記述することが非常に多いです。コーディング規約、コード例、導入手順・・・などなど。
作業指示なども、Kobito で Markdown で書いて、相手にも Kobito のインストールを強制し、エクスポートしたテキスト ファイルを渡し、Kobito で開いて確認してね、というノリでやってます。仮に、Kobito を使わずに、一般的なテキスト エディタで見ても問題はないでしょうし。
他にも、打ち合わせでは Kobito で書いた内容をモニタに映して説明しています。
バージョン管理システムで文書も同時に管理する事を考えると、Markdown は差分把握に良い形式です。ゆえに GitHub の README も Markdown なのでしょう。
コピペの際にも便利ですね。テキスト ベースの場合、コピー元の書式にとらわれる事が無いので。変に凝ったツールでは、コピー元の書式までコピーしてペーストしてしまうので、かなりウザいです。
・・・って言うか、書いていて楽しいです。プレビューで整形結果をリアルタイムに見ながら、テキストでサクサクと情報を記述できるので。
欲を言うならば、Qiita 同様に目次も表示してくれると良いのですが・・・。後は、Qiita とは独立して、Kobito 単体でファイルを階層化できればなぁ、と。
2014/10/11
2013/08/26
FF14 製作支援ツールβ公開
Android アプリとして実装した FF14 製作支援ツール(FF14CST)ですが、Google ドライブにてβバージョンとして公開しておきます。
なお、アプリからはパーミッション許可を一切要求しません。
オープンβでは、幻術士 Lv20、全製作クラス Lv15、採掘師と園芸師 Lv15 まで上げ、その範囲でデータを自身で確認しましたが、それ以上については下記サイトで公開されている情報を元にしています。
後は、eLeMeN の製作レシピ データでは、完成時の数量が未記載であるため、ここをどうしようかなと思っている所です。
主に、数量が問題となるのは調理レシピですが、今は完成時数量 1 で入力してあります。単にレシピを検索するだけならば、完成時の数量はどうでも良いのですが、アプリで実装した原価計算機能を考えると無視できず、困っている所です。
現状、全レシピを確認できるのはレガシー先輩の一部だけだと思うので、どうしたものですかねぇ・・・。
スクリーン キャプチャ
![]() |
| 製作詳細 |
ダウンロード
アプリは以下からダウンロードできます。FF14CST(β) version 1.0.0公開フォルダは以下です。
FF14CST公開フォルダ
インストール時の注意
Google Play からのダウンロードではないため、Android 端末の設定で『提供元不明のアプリ』の項目を『許可』に設定しなければインストールできません。なお、アプリからはパーミッション許可を一切要求しません。
データに関する注意
アプリ内データには、- オープンβのデータに基いていること
- 自分自身で確認していないデータが大量にあること
オープンβでは、幻術士 Lv20、全製作クラス Lv15、採掘師と園芸師 Lv15 まで上げ、その範囲でデータを自身で確認しましたが、それ以上については下記サイトで公開されている情報を元にしています。
eLeMeN - FF14
FF14 Tamachang
新生 FF14 ギャザクラ生活もちろん、上記サイト管理者の方々には多大な敬意を払っていますが、やはり人の手でデータを入力していることから入力ミスがあるでしょう。それらを元に僕がデータを複製するため、アプリ内データにはより多くの入力ミスが発生している可能性があるわけです。データベース作成のための専用アプリケーションを Windows で実装して利用しているので、ある程度のミスは軽減できているとは思うのですが、データ入力はちょっと頭がおかしくなりそうな作業でもあり、ミスはどうしても発生するだろうなぁと。
後は、eLeMeN の製作レシピ データでは、完成時の数量が未記載であるため、ここをどうしようかなと思っている所です。
主に、数量が問題となるのは調理レシピですが、今は完成時数量 1 で入力してあります。単にレシピを検索するだけならば、完成時の数量はどうでも良いのですが、アプリで実装した原価計算機能を考えると無視できず、困っている所です。
現状、全レシピを確認できるのはレガシー先輩の一部だけだと思うので、どうしたものですかねぇ・・・。
2013/08/05
FF14 製作支援ツールを開発中
FF14 の製作支援ツールを Android で作っています。ひとまず、製作レシピ、採集場所、各アイテムの販売場所の参照など、基礎部分の実装を終え、データを打ち込んで大雑把にテストを終えた所です。
記念撮影として、画面の雰囲気を以下に貼ってみます。
仮想デバイスでのキャプチャなのでチープ感が漂っていますが、実機ではもう少しマシです・・・多分。
基本的には、製作活動の「支援」に特化して設計しています。データの全てを網羅する気はないと言いますか。後は、各アイテムのアイコンを揃えたりなどの、作業量的に無理な部分は諦めています。
技術的な全体像としては、制作関連データは SQLite でデータベース化、Android アプリ側でデータベースを参照して表示、データベースは Windows アプリケーションで構築、という感じです。Windows アプリケーションとして実装したデータベース メンテナンス画面からデータを投入し、Android アプリのアセットへデータベースをそのまま配備し、アプリ起動時に Android 仕様のデータベース位置へ複製という流れですね。メンテナンス アプリケーションは C# WPF で実装しています。
メンテナンス アプリケーションは、下図のような感じです。
参照系機能の実装が完了しただけであり、これから自分が必要とするプラスアルファの機能を実装することになります。オープン β 中に、ゲームをプレイしながら使いやすさの改善を試みたいので、それまでにプラスアルファの機能の実装を終えてしまいたい所です。まぁ、今はまだ例外で落ちたりするので、デバッグがまだまだ不十分なのですが。
せっかく実装したので、適当なタイミングでストアへ公開するとは思いますが、多くの人には使えないアプリだろうと思っています。僕が FF14 に張り付いてデータのバージョン アップに対応することはできないので、アプリ内データを最新へ保つことが難しいわけで。そもそも僕は飽き症で、そう長くは FF14 をプレイしないであろう事も問題です。
まぁ、おいおい考えていこうかと思っています。
記念撮影として、画面の雰囲気を以下に貼ってみます。
![]() |
| レベル帯からの製作選択 |
![]() |
| 材料確認 |
![]() |
| 販売品確認 |
仮想デバイスでのキャプチャなのでチープ感が漂っていますが、実機ではもう少しマシです・・・多分。
基本的には、製作活動の「支援」に特化して設計しています。データの全てを網羅する気はないと言いますか。後は、各アイテムのアイコンを揃えたりなどの、作業量的に無理な部分は諦めています。
技術的な全体像としては、制作関連データは SQLite でデータベース化、Android アプリ側でデータベースを参照して表示、データベースは Windows アプリケーションで構築、という感じです。Windows アプリケーションとして実装したデータベース メンテナンス画面からデータを投入し、Android アプリのアセットへデータベースをそのまま配備し、アプリ起動時に Android 仕様のデータベース位置へ複製という流れですね。メンテナンス アプリケーションは C# WPF で実装しています。
メンテナンス アプリケーションは、下図のような感じです。
![]() |
| 製作管理 |
![]() |
| 商品管理 |
参照系機能の実装が完了しただけであり、これから自分が必要とするプラスアルファの機能を実装することになります。オープン β 中に、ゲームをプレイしながら使いやすさの改善を試みたいので、それまでにプラスアルファの機能の実装を終えてしまいたい所です。まぁ、今はまだ例外で落ちたりするので、デバッグがまだまだ不十分なのですが。
せっかく実装したので、適当なタイミングでストアへ公開するとは思いますが、多くの人には使えないアプリだろうと思っています。僕が FF14 に張り付いてデータのバージョン アップに対応することはできないので、アプリ内データを最新へ保つことが難しいわけで。そもそも僕は飽き症で、そう長くは FF14 をプレイしないであろう事も問題です。
まぁ、おいおい考えていこうかと思っています。
2013/07/01
ブロック地形 SharpDX 版進捗
基本の移植は概ね終わったという感じです。合わせて、SharpDX 用自作フレームワークで作成した各種機能を強引に適用し、フレームワークのデバッグを同時に行なっているという感じです。やはり、単純なサンプル実装では現れないバグがチラホラと出てきますね。
下図、記念撮影。
上図シーンは、Deferred Rendering、PSSM によるシャドウ マップ分割、環境光閉塞、被写界深度、ポストプロセスによる線形フォグ、といった感じです。
この状態で 60 FPS を維持できなくなりました。デバッグ ビルドで 40-50 FPS くらいですね。シャドウ マッピングと環境光閉塞を適用しない、あるいは、チャンク描画範囲をもう少し狭めれば、60 FPS を維持できるのですが、なるべく負荷の高い状態で実装する方が、チューニングについて学ぶことも多いような気がしています。
分かる範囲でも、投影オブジェクトの選別ロジックを大雑把にしていること、オクルージョン カリングを大雑把にしていることなど、問題は多々あります。速度の問題以外にも、期待する影を描画できていない問題もありますが。
期待していたよりも十分な速度を維持しているので、ひとまず自作フレームワークをそのまま使い、品質を上げていく気分になりました。とても大変ですが、自分のコードならば、誤りがあった場合でも原因の特定と修正が容易であるという利点がありますからね。
でまぁ、Minecraft 風地形の実装はここで終わりにし、その実装を通じて本来やってみたかった実装の方法が見えてきたので、そちらに移行しようかなぁ・・・などと考えています。
下図、記念撮影。
上図シーンは、Deferred Rendering、PSSM によるシャドウ マップ分割、環境光閉塞、被写界深度、ポストプロセスによる線形フォグ、といった感じです。
この状態で 60 FPS を維持できなくなりました。デバッグ ビルドで 40-50 FPS くらいですね。シャドウ マッピングと環境光閉塞を適用しない、あるいは、チャンク描画範囲をもう少し狭めれば、60 FPS を維持できるのですが、なるべく負荷の高い状態で実装する方が、チューニングについて学ぶことも多いような気がしています。
分かる範囲でも、投影オブジェクトの選別ロジックを大雑把にしていること、オクルージョン カリングを大雑把にしていることなど、問題は多々あります。速度の問題以外にも、期待する影を描画できていない問題もありますが。
期待していたよりも十分な速度を維持しているので、ひとまず自作フレームワークをそのまま使い、品質を上げていく気分になりました。とても大変ですが、自分のコードならば、誤りがあった場合でも原因の特定と修正が容易であるという利点がありますからね。
でまぁ、Minecraft 風地形の実装はここで終わりにし、その実装を通じて本来やってみたかった実装の方法が見えてきたので、そちらに移行しようかなぁ・・・などと考えています。
2013/06/25
ブロック地形実装を XNA から SharpDX へ移植中
SharpDX ベースの自作フレームワークのために作成したサンプル アプリケーションも問題なく稼働しているように思えたので、XNA で実装したブロック地形プログラムを自作フレームワークへ移植しています。
自作フレームワークは XNA 非互換、および、シェーダ モデル 5.0 が基本である事から、移植作業に難儀していますが、概ね移植を終えて稼働し始めた所です。
まともに稼働し始めて気になる点が、やけに処理が早いという点です。本来ならば喜ぶべき事ですが、納得できる理由が分からないため、逆に怖いという感じです。
少なくとも、XNA 公式のインスタンシング サンプルを自作フレームワークに移植した時に、自作フレームワークは XNA よりも低速である事を、FPS の数値として自分で認識しています。しかし、ブロック地形実装では明らかに XNA よりも高速です。
DirectX 11 では GPU が上手いことやってくれているんですかねぇ・・・。
自作フレームワークは XNA 非互換、および、シェーダ モデル 5.0 が基本である事から、移植作業に難儀していますが、概ね移植を終えて稼働し始めた所です。
まともに稼働し始めて気になる点が、やけに処理が早いという点です。本来ならば喜ぶべき事ですが、納得できる理由が分からないため、逆に怖いという感じです。
少なくとも、XNA 公式のインスタンシング サンプルを自作フレームワークに移植した時に、自作フレームワークは XNA よりも低速である事を、FPS の数値として自分で認識しています。しかし、ブロック地形実装では明らかに XNA よりも高速です。
DirectX 11 では GPU が上手いことやってくれているんですかねぇ・・・。
2013/06/02
Deferred Shadow Mapping を実装していた
Deferred Rendering によるシャドウ マッピングのための機能を、自作フレームワークに実装していました。ただし、MRT は使っていませんし、Deferred Rendering のための綺麗な枠組みも作っていません。ポストプロセスについては一つの枠組みを実装してあるので、これに乗せて動かしているという感じです。
以下、記念撮影です。まず、シーンの深度マップ。
続いて、シャドウ マップを書き込んだ閉塞マップ。
閉塞マップと呼ぶべきか否か、専門的な名称が分かりませんが、シャドウ マップから得られる深度と、シーンの深度マップから再構築した座標から得られる深度を比較し、ライトが遮られているか否か、すなわち、影があるか否かを描画したシーンです。生成されるテクスチャは SSAO で生成する閉塞マップと同じであるため、関連する機能の一部を共通化して閉塞マップと呼んでいます。
最後に、影を合成した最終シーン。
シャドウ マップ サイズ 1024x1024、PSSM 分割数 3、一般的なライト カメラを使用、PCF 7x7 適用です。
PCF は過去に実装済みでしたが、モデル描画のためのシェーダに実装すると、関連するシェーダの至る所で冗長に記述することになるため、先のシャドウ マッピングの実装では避けていました。今回は、閉塞マップ描画シェーダにのみ記述すれば済むため、あらためて再実装したという所です。
今回の実装は、それ程には難しいものではなかったはずですが、本筋とは関係ない所で長時間ハマってました。定数バッファへ渡す C# 側構造体のレイアウトを誤り、これに気付かずにハマり続けるという失敗を、これまで何度も繰り返しています・・・。
以下、記念撮影です。まず、シーンの深度マップ。
![]() |
| 深度マップ |
続いて、シャドウ マップを書き込んだ閉塞マップ。
![]() |
| 閉塞マップ |
閉塞マップと呼ぶべきか否か、専門的な名称が分かりませんが、シャドウ マップから得られる深度と、シーンの深度マップから再構築した座標から得られる深度を比較し、ライトが遮られているか否か、すなわち、影があるか否かを描画したシーンです。生成されるテクスチャは SSAO で生成する閉塞マップと同じであるため、関連する機能の一部を共通化して閉塞マップと呼んでいます。
最後に、影を合成した最終シーン。
![]() |
| 最終シーン |
シャドウ マップ サイズ 1024x1024、PSSM 分割数 3、一般的なライト カメラを使用、PCF 7x7 適用です。
PCF は過去に実装済みでしたが、モデル描画のためのシェーダに実装すると、関連するシェーダの至る所で冗長に記述することになるため、先のシャドウ マッピングの実装では避けていました。今回は、閉塞マップ描画シェーダにのみ記述すれば済むため、あらためて再実装したという所です。
今回の実装は、それ程には難しいものではなかったはずですが、本筋とは関係ない所で長時間ハマってました。定数バッファへ渡す C# 側構造体のレイアウトを誤り、これに気付かずにハマり続けるという失敗を、これまで何度も繰り返しています・・・。
2013/06/01
簡単な雲を実装
過去行った実装に雲エフェクトがあったので、これも自作フレームワークへ移植していました。しかし、どうやらこれも何かの理由で破棄し、面倒な事にシェーダ コードしか残されていないという有様。元ネタも記録されておらず推測から再実装してみましたが、期待する効果を得られなかったので、結局、一から考えなおして作っていました。
実装の流れは極めて簡単なもので、以下のような手順で行いました。
1. 雲の体積マップ(R 値のみ)をノイズで複数枚生成
2. 各体積マップをテクスチャ座標をずらしながら参照
3. 各体積に密度を掛けつつ合成して色を決定
以下、スクリーン ショットです。
極めてチープ感が漂っていますが、「質を問わないので雲が欲しい」という場合に、僕としてはこれで良いかなと。完全な平面へエフェクトを適用しているため、チープ感が増しているのだとは思いますが、曲がった平面のための頂点データを自動作成することが面倒だったので、今はこれで良いかという感じです。
雲の体積マップと呼んでいるものは、雲の高さ、あるいは、雲の密度として置き換えて考えても良い気がします。実際、ハイトマップを生成する手順と同じ手順で体積マップを生成していますし。
ノイズの種類としては、Billow 型フラクタルが雲の雰囲気を出すと良く言われ、実際に雲と見做せる形状でノイズが生成されるものの、僕が作成したエフェクトでの合成には不向きのようで、結局は SumFractal で生成しました。ただし、雲の密度を下げ、塊となった雲を幾つか出すような状況では Billow の方が適していた感じもするので、状況に応じてノイズを調整する事になるのであろうと思います。
何にせよ、ノイズ生成ライブラリを自作したのは本当に良かったですね。簡単に試せる状態は非常に便利です。
で、作成した水面エフェクトへ反射させてみたいという理由も含めて実装したので、反射させてみました。
水面で反射していますが、ほぼ見えず、期待した感じではありません。反射は、水面の法線と視線が垂直に近くなるほどに強くなると思うので、視点付近の水面では雲が弱く映り、遠方で強く映る状態は正しいのだと思いますが・・・もう少し映り込む状態を正直期待していました。そもそも、雲自体の色が薄いので、しょうがない気もします。あるいは、水面に適用する水の色が駄目なんですかねぇ・・・。
強引に調整できそうではあるものの、拘ると時間が掛かりそうなので、今はこれで良いとしました。色と輪郭が明確なオブジェクトの反射は期待した通りに反射されていますし。
実装の流れは極めて簡単なもので、以下のような手順で行いました。
1. 雲の体積マップ(R 値のみ)をノイズで複数枚生成
2. 各体積マップをテクスチャ座標をずらしながら参照
3. 各体積に密度を掛けつつ合成して色を決定
以下、スクリーン ショットです。
![]() |
| 雲エフェクト |
極めてチープ感が漂っていますが、「質を問わないので雲が欲しい」という場合に、僕としてはこれで良いかなと。完全な平面へエフェクトを適用しているため、チープ感が増しているのだとは思いますが、曲がった平面のための頂点データを自動作成することが面倒だったので、今はこれで良いかという感じです。
雲の体積マップと呼んでいるものは、雲の高さ、あるいは、雲の密度として置き換えて考えても良い気がします。実際、ハイトマップを生成する手順と同じ手順で体積マップを生成していますし。
ノイズの種類としては、Billow 型フラクタルが雲の雰囲気を出すと良く言われ、実際に雲と見做せる形状でノイズが生成されるものの、僕が作成したエフェクトでの合成には不向きのようで、結局は SumFractal で生成しました。ただし、雲の密度を下げ、塊となった雲を幾つか出すような状況では Billow の方が適していた感じもするので、状況に応じてノイズを調整する事になるのであろうと思います。
何にせよ、ノイズ生成ライブラリを自作したのは本当に良かったですね。簡単に試せる状態は非常に便利です。
で、作成した水面エフェクトへ反射させてみたいという理由も含めて実装したので、反射させてみました。
![]() |
| 雲の反射 |
水面で反射していますが、ほぼ見えず、期待した感じではありません。反射は、水面の法線と視線が垂直に近くなるほどに強くなると思うので、視点付近の水面では雲が弱く映り、遠方で強く映る状態は正しいのだと思いますが・・・もう少し映り込む状態を正直期待していました。そもそも、雲自体の色が薄いので、しょうがない気もします。あるいは、水面に適用する水の色が駄目なんですかねぇ・・・。
強引に調整できそうではあるものの、拘ると時間が掛かりそうなので、今はこれで良いとしました。色と輪郭が明確なオブジェクトの反射は期待した通りに反射されていますし。
2013/05/30
ポストプロセスでフォグ
水面シェーダにフォグ処理を追加しておこうと試み始めたのですが、それをやり始めると、モデル描画に使用する他の様々なシェーダにおいてもフォグ処理を記述しなければならない状況に陥ります。これは嫌だなぁと。フォグの ON/OFF なども考慮すると、更にコードが汚くなるわけで。
そこで、ポストプロセスによるフォグを実装していました。検索してみると「今時はフォグもポストプロセスですよ」などと書いている海外の人もいましたが、素人の僕には実際がどうなのかは分かりません。
ボリューム フォグは既に実装していたので、線形フォグ、指数フォグ、高さフォグを実装していました。基本は、ポストプロセスのピクセル シェーダにて線形深度マップを参照し、深度からビュー空間における位置を再構築して利用する流れです。ただし、高さフォグでは、ワールド空間における y 値が必要となるため、更に逆ビュー行列を掛けて情報を得ています。
ポストプロセスならば簡単に処理を ON/OFF できるので、各フォグを組み合わせて遊んでみましたが、組み合わせる事により意外な味が出るものですね。
そこで、ポストプロセスによるフォグを実装していました。検索してみると「今時はフォグもポストプロセスですよ」などと書いている海外の人もいましたが、素人の僕には実際がどうなのかは分かりません。
ボリューム フォグは既に実装していたので、線形フォグ、指数フォグ、高さフォグを実装していました。基本は、ポストプロセスのピクセル シェーダにて線形深度マップを参照し、深度からビュー空間における位置を再構築して利用する流れです。ただし、高さフォグでは、ワールド空間における y 値が必要となるため、更に逆ビュー行列を掛けて情報を得ています。
ポストプロセスならば簡単に処理を ON/OFF できるので、各フォグを組み合わせて遊んでみましたが、組み合わせる事により意外な味が出るものですね。
2013/05/29
水面描画、記念撮影。
水面描画への再挑戦ですが、何とか想定した実装になった所です。以下、記念撮影です。
一応、テクスチャ座標を時間でずらして水の流れを起こしていますが、静止画では分かるわけがないですね。
法線マップを手作業で作成することは嫌いであるため、Perlin noise で生成しています。実装前にはノイズ設定が難しいであろうと想像していたのですが、単純に SumFractal で合成しただけでそれらしくなりました。
ノイズからの法線マップの作成は、ノイズで波を表すハイトマップを作成し、ハイトマップから勾配を算出して法線を構築するという手順です。なお、作成した法線マップはシームレス テクスチャとして自動生成しているので、アドレス モードを Wrap にして貼り付けています。
上図は少し角度を付けてみた状態です。水面は任意の面に描画できるようにしています。
波の影響を大きくしているため見辛いですが、屈折と反射を実装しています。反射の度合いには、Schlick の近似によるフレネルの式 を用いています。
ネット上では、フレネルの式から得られる値を係数として、反射と屈折を線形補間した値を水面の色としているサンプルを多く見かけましたが、僕は反射にのみ適用しています。屈折に関しては、視点から一定の距離が離れる程に、および、視線が水面に並行になる程に減衰するような仕組みにしています。この基本形は、GPU Gems 2 - Chapter 19. Generic Refraction Simulation に従っています。
水面を垂直にしてみると上図のような感じです。だから何だという話ですが。
上図は、書籍『ゲーム エフェクト マニアックス』に掲載されていた波生成の実装です。これは、アプリケーションからテクスチャ上に波紋を発生させて法線マップを作る実装です。JoJo ファンにはたまらない実装ですね。
と、何とか形になったなぁと。
ただ、テストをしていると定期的かつ瞬間的に描画がカクつく現象が起こっていて、フレームワーク側の問題であることが明らかであり、厄介な事になってきたなぁと感じている所です。実際にはとうに気付いていたんですが、見ないふりをしていた所があり、このまま見ないふりを続けるかもしれません。
どうにもならなければ、XNA に戻るだけです。
一応、テクスチャ座標を時間でずらして水の流れを起こしていますが、静止画では分かるわけがないですね。
法線マップを手作業で作成することは嫌いであるため、Perlin noise で生成しています。実装前にはノイズ設定が難しいであろうと想像していたのですが、単純に SumFractal で合成しただけでそれらしくなりました。
ノイズからの法線マップの作成は、ノイズで波を表すハイトマップを作成し、ハイトマップから勾配を算出して法線を構築するという手順です。なお、作成した法線マップはシームレス テクスチャとして自動生成しているので、アドレス モードを Wrap にして貼り付けています。
上図は少し角度を付けてみた状態です。水面は任意の面に描画できるようにしています。
波の影響を大きくしているため見辛いですが、屈折と反射を実装しています。反射の度合いには、Schlick の近似によるフレネルの式 を用いています。
ネット上では、フレネルの式から得られる値を係数として、反射と屈折を線形補間した値を水面の色としているサンプルを多く見かけましたが、僕は反射にのみ適用しています。屈折に関しては、視点から一定の距離が離れる程に、および、視線が水面に並行になる程に減衰するような仕組みにしています。この基本形は、GPU Gems 2 - Chapter 19. Generic Refraction Simulation に従っています。
水面を垂直にしてみると上図のような感じです。だから何だという話ですが。
上図は、書籍『ゲーム エフェクト マニアックス』に掲載されていた波生成の実装です。これは、アプリケーションからテクスチャ上に波紋を発生させて法線マップを作る実装です。JoJo ファンにはたまらない実装ですね。
と、何とか形になったなぁと。
ただ、テストをしていると定期的かつ瞬間的に描画がカクつく現象が起こっていて、フレームワーク側の問題であることが明らかであり、厄介な事になってきたなぁと感じている所です。実際にはとうに気付いていたんですが、見ないふりをしていた所があり、このまま見ないふりを続けるかもしれません。
どうにもならなければ、XNA に戻るだけです。
2013/05/27
水面描画への再挑戦
過去のブロック地形動画の際に、海や湖を表現するために水面のエフェクトを組んでいたのですが、これを汎用化して自作 SharpDX フレームワークに載せようと試みていました。基本的には、波を表すための法線マップを事前にテクスチャとして用意するという方法が嫌いなため、自動生成して利用する枠組みにしています。
過去の実装から思い出していたのですが、元は反射と屈折を考慮した水面を実装していたにも関わらず、何かの理由で反射と屈折を止めた記憶があるなぁ・・・と。
屈折効果に用いる水中シーンを描画するために、GraphicsDevice.ClipPlanes の設定により水面より上をクリップするという XNA のチュートリアル サイトの情報があり、過去の自分はこれを参考に実装したと思われますが、この機能は XNA 4.0 で失われています。恐らく、僕は移行方法が分からずに諦めたのではないかと。
XNA 4.0 における対応としては、クリップ面をシェーダへ渡し、頂点からクリップ面までの距離を算出し、clip 関数を用いて距離が 0 以下 (頂点がクリップ面の裏側) ならばピクセル シェーダの出力を抑制してしまう方法かと思います。
シェーダ モデル 4.0 以上ならば、算出した距離を SV_ClipDistance セマンティクスでピクセル シェーダへ渡せば自動的にクリップしてもらえるようなので、自作フレームワークではこれを利用しました。
他には、頂点シェーダの属性として [clipplanes] でクリップ面を指定すると、SV_ClipDistance に関わるコードを書かずに済むらしいのですが、これは使わない事にしました。僕は、シェーダのコンパイル時警告をエラーとして扱っていますが、clipplanes を指定するとクリッピングに関する頂点シェーダ出力の初期化が完全ではないとの警告が出てしまい、これを解決する術が分からずに断念しました。どうにも、clipplanes 属性の利用に関する情報が少なすぎて。
ひとまず、一般的な水の表現は実装できましたが、水が XZ 平面上にある必要もないと思い、自由に回転させた平面に水面エフェクトを適用しようと試みている所です。
とまぁ、気付いたらまた壮大に脱線し始めたと思うものの、どこかの時点で自身の知識の整理はすべきであり、なおかつ、更に一歩進むべきとの思いもあり。
過去の実装から思い出していたのですが、元は反射と屈折を考慮した水面を実装していたにも関わらず、何かの理由で反射と屈折を止めた記憶があるなぁ・・・と。
屈折効果に用いる水中シーンを描画するために、GraphicsDevice.ClipPlanes の設定により水面より上をクリップするという XNA のチュートリアル サイトの情報があり、過去の自分はこれを参考に実装したと思われますが、この機能は XNA 4.0 で失われています。恐らく、僕は移行方法が分からずに諦めたのではないかと。
XNA 4.0 における対応としては、クリップ面をシェーダへ渡し、頂点からクリップ面までの距離を算出し、clip 関数を用いて距離が 0 以下 (頂点がクリップ面の裏側) ならばピクセル シェーダの出力を抑制してしまう方法かと思います。
シェーダ モデル 4.0 以上ならば、算出した距離を SV_ClipDistance セマンティクスでピクセル シェーダへ渡せば自動的にクリップしてもらえるようなので、自作フレームワークではこれを利用しました。
他には、頂点シェーダの属性として [clipplanes] でクリップ面を指定すると、SV_ClipDistance に関わるコードを書かずに済むらしいのですが、これは使わない事にしました。僕は、シェーダのコンパイル時警告をエラーとして扱っていますが、clipplanes を指定するとクリッピングに関する頂点シェーダ出力の初期化が完全ではないとの警告が出てしまい、これを解決する術が分からずに断念しました。どうにも、clipplanes 属性の利用に関する情報が少なすぎて。
ひとまず、一般的な水の表現は実装できましたが、水が XZ 平面上にある必要もないと思い、自由に回転させた平面に水面エフェクトを適用しようと試みている所です。
とまぁ、気付いたらまた壮大に脱線し始めたと思うものの、どこかの時点で自身の知識の整理はすべきであり、なおかつ、更に一歩進むべきとの思いもあり。
2013/05/20
SSAO に再挑戦していた
数年前に SSAO (Screen Space Ambient Occlusion) を実装しましたが、自身の実装と解釈に誤りがあると認識しつつも放置したので、SharpDX ベースの自作フレームワークに入れるために、再度、納得できる質まで改善していました。
数年前に参考にした実装は下記サイトであり、今回もこれをベースにしました。
今回は、上記サイトの実装をベースにしつつ、深度を線形深度マップ (Linear depth map) へ置き換え、線形深度からビュー空間座標を再構築して判定するような実装を組んでいました。その他、シェーダ内でハードコードされた球面内サンプル点や、テクスチャ ファイルとして用意しているランダム法線マップをなどを C# 側で生成してシェーダへ渡したりしています。
相変わらず、テストに使用しているシーンが簡素過ぎますが、以下、ひとまずは納得できた結果の画像です。
レイによるサンプル数を 8 として SSAO マップを生成しています。生成された SSAO マップへのブラーでは、バイラテラル フィルタの変形バージョンを 3 回適用しています。1 回の適用でもそれなりですが、3 回程適用すると十分な綺麗さが出る感じがします。なお、単純に色の差異でバイラテラル フィルタを適用すると期待するブラーにはならないので、深度マップを参照して深度の差異で重みを変化させています。加えて、法線の差異でも重み付けを変化させているため、厳密にはバイラテラルではないのだろうと思います。法線を考慮せずとも十分なブラーを得られますが、法線差異を制御するための標準偏差を色々と変化させてみると微妙な調整ができるので、これで良いとしています。
負荷を下げるために SSAO マップのサイズを小さくし、シーンと合成する前にアップサンプリングしてみたりなどもしましたが、単純なアップサンプリングではシーンとの合成時に解像度の差異から汚くなる部分が出てくるため、少し考慮が必要なのだろうと感じています。
数年前に参考にした実装は下記サイトであり、今回もこれをベースにしました。
Game Rendering - SSAO: http://www.gamerendering.com/2009/01/14/ssao/大まかな処理の流れは、Ray Marching Ambient Occlusion を基礎とし、法線による平面間に対する判定を除去しているのだと思います (実装上は法線差による閉塞度に対する重み付けの調整)。
今回は、上記サイトの実装をベースにしつつ、深度を線形深度マップ (Linear depth map) へ置き換え、線形深度からビュー空間座標を再構築して判定するような実装を組んでいました。その他、シェーダ内でハードコードされた球面内サンプル点や、テクスチャ ファイルとして用意しているランダム法線マップをなどを C# 側で生成してシェーダへ渡したりしています。
相変わらず、テストに使用しているシーンが簡素過ぎますが、以下、ひとまずは納得できた結果の画像です。
![]() |
| ブラー前 SSAO マップ |
![]() |
| ブラー後 SSAO マップ |
![]() |
| SSAO 適用シーン |
負荷を下げるために SSAO マップのサイズを小さくし、シーンと合成する前にアップサンプリングしてみたりなどもしましたが、単純なアップサンプリングではシーンとの合成時に解像度の差異から汚くなる部分が出てくるため、少し考慮が必要なのだろうと感じています。
追記: と思っていたら、どうも僕のダウンサンプリング実装に誤りがあり、修正して適用してみると、悪くない結果でした。3D グラフィックスは、僅かな値の差ではそれなりに稼働してしまうので、デバッグが怖いですね。何にせよ、納得できる所まで持ってこれて、心底安心できました。
2013/05/10
Volumetric Light Scattering に再挑戦していた
数年前に公開したブロック地形動画では Volumetric Light Scattering (God Ray、Crepuscular Ray) を実装していましたが、当時はまだ理解できていなかった部分があったため、再実装していました。あれから新たについた知識を元に実装すれば、また新たな側面が見えてくるであろうと考えて。
Volumetric Light Scattering については以下に説明があります。
今回の実装では、ライト閉塞マップを作成し、ライト閉塞マップに対して Volumetric Light Scattering シェーダを適用してライト散乱マップを作成し、ライト散乱マップと通常シーンを加算混合するという手順でシーンを生成しています。
スカイ スフィアを描画し (方向性光源の方向が示す箇所に太陽をシェーダで描画)、後は立方体を適用に並べています。ライト閉塞マップやライト散乱マップを黒色をベースに生成していますが、これは最後に通常シーンと加算混合するためです。
シーンの選択に問題がある気はしますが、一応、実装は上手くいったようであると感じています。
今回は、SharpDX での自作フレームワーク上で実装しているので、ソースは公開しません。と言っても、GitHub 上でリポジトリが公開状態ではありますが (Libra リポジトリに含まれています)。自作フレームワーク上の実装では SharpDX としてのサンプルにもならないですし、XNA へまた移植するのも疲れるしなぁと。
XNA 上でのサンプルとしては、下記サイトが良いと思います (ソースコードも公開されています)。
他には、ライト閉塞マップに対して Radial Blur を適用してライト放射マップとし、シーンへ混合する方法もあるようです。そもそも、Volumetric Light Scattering シェーダは、ライト関連の調整を行いやすいように Radial Blur を変更した物と見做すこともできるかもしれません。
Volumetric Light Scattering については以下に説明があります。
NVIDIA GPU Gems 3 - Chapter 13. Volumetric Light Scattering as a Post-ProcessVolumetric Light Scattering は日本語で何と言うのでしょうか。端的には光芒だとは思いますが。Scatter は散乱なのでしょうか。特に、Volumetric の適切な訳がいつも分かりません。
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch13.html
今回の実装では、ライト閉塞マップを作成し、ライト閉塞マップに対して Volumetric Light Scattering シェーダを適用してライト散乱マップを作成し、ライト散乱マップと通常シーンを加算混合するという手順でシーンを生成しています。
![]() |
| Volumetric Light Scattering 効果なし |
![]() |
| Volumetric Light Scattering 効果あり |
シーンの選択に問題がある気はしますが、一応、実装は上手くいったようであると感じています。
今回は、SharpDX での自作フレームワーク上で実装しているので、ソースは公開しません。と言っても、GitHub 上でリポジトリが公開状態ではありますが (Libra リポジトリに含まれています)。自作フレームワーク上の実装では SharpDX としてのサンプルにもならないですし、XNA へまた移植するのも疲れるしなぁと。
XNA 上でのサンプルとしては、下記サイトが良いと思います (ソースコードも公開されています)。
XNA-UK: Randomchaos Archivs - 2D Crepuscular (God) Rays上記サイトでは 2D アプリケーションとして実装していますが、Volumetric Light Scattering シェーダはテクスチャに対して適用するので、3D でも同じ事です。
http://xnauk-randomchaosblogarchive.blogspot.co.uk/2012/10/2d-crepuscular-god-rays.html
他には、ライト閉塞マップに対して Radial Blur を適用してライト放射マップとし、シーンへ混合する方法もあるようです。そもそも、Volumetric Light Scattering シェーダは、ライト関連の調整を行いやすいように Radial Blur を変更した物と見做すこともできるかもしれません。
2013/04/28
LiSPSM にリベンジしていた
数年前、LiSPSM (Light Space Perspective Shadow Maps) の実装を試みたものの、どうにもおかしな振る舞いとなる部分を直せずに利用を断念していましたが、納得していなかったので再挑戦していました。
LiSPSM 公式サイトでは C コードと C++ コードをサンプルとして公開しており、過去の挑戦では C コードを参考にしていましたが、今回は C++ コードを元に実装してみました。実際には、Ogre (オープンソースの 3D グラフィックス エンジン) も参考にしています。Ogre では、LiSPSMShadowCameraSetup クラスが LiSPSM のロジックにあたり、コードの基本はオリジナルと同じです。
で、方向性光源 (directional light) についてのみの実装ですが、何とかまともに動くようになりました。基本はオリジナル コードの模倣ですが、どうもオリジナルのままではカメラを光源の方向へ向けた場合に描画が上手くいかないため、Ogre のコードを真似ました。後は、Ogre では、カメラとライトが概ね平行になる場合に N の算出に補正を掛けていたので、これは良いと思い、形を変えて真似たという所です。
まぁ、自分で納得するか否かの問題で再挑戦しただけであり、恐らく LiSPSM は使わないのですが。僕は PSSM (Parallel Split Shadow Maps) を使う前提としているため、この場合には LiSPSM を用いる必要がそれ程には無いと言いますか。
いずれにせよ、LiSPSM のサンプル コードが少ないことで難儀したので、整理して公開しようと考えています。今の実装は自作 SharpDX ラッパー上の実装であり、サンプル公開として適さないため、XNA コードとして再実装しようかなと。ただ、凸体の構築ロジックではバカスカと新規オブジェクトを生成しているので、これを何とかしてからですかねぇ・・・。
LiSPSM 公式サイトでは C コードと C++ コードをサンプルとして公開しており、過去の挑戦では C コードを参考にしていましたが、今回は C++ コードを元に実装してみました。実際には、Ogre (オープンソースの 3D グラフィックス エンジン) も参考にしています。Ogre では、LiSPSMShadowCameraSetup クラスが LiSPSM のロジックにあたり、コードの基本はオリジナルと同じです。
で、方向性光源 (directional light) についてのみの実装ですが、何とかまともに動くようになりました。基本はオリジナル コードの模倣ですが、どうもオリジナルのままではカメラを光源の方向へ向けた場合に描画が上手くいかないため、Ogre のコードを真似ました。後は、Ogre では、カメラとライトが概ね平行になる場合に N の算出に補正を掛けていたので、これは良いと思い、形を変えて真似たという所です。
まぁ、自分で納得するか否かの問題で再挑戦しただけであり、恐らく LiSPSM は使わないのですが。僕は PSSM (Parallel Split Shadow Maps) を使う前提としているため、この場合には LiSPSM を用いる必要がそれ程には無いと言いますか。
いずれにせよ、LiSPSM のサンプル コードが少ないことで難儀したので、整理して公開しようと考えています。今の実装は自作 SharpDX ラッパー上の実装であり、サンプル公開として適さないため、XNA コードとして再実装しようかなと。ただ、凸体の構築ロジックではバカスカと新規オブジェクトを生成しているので、これを何とかしてからですかねぇ・・・。
2013/04/01
2013/03/23
XNB モデル データの読み込みと描画まで到達
XNB ファイルを読み込み、自分のモデル クラスへデシリアライズし、描画が可能という所まで来ました。
公式サイトの XNB フォーマットの説明にコード例(C++)も含まれているので、読み込みに関する実装は簡単でしたが、自分が作成した Texture2D クラスのバグと言うか、自分の知識不足のために難儀しました・・・。
ただ、読み込めるデータには制約を設け、例えば、モデルで扱えるエフェクトを BasicEffect に限定したりなどしています。また、今はモデルを読み込みたいだけなので、その他は無視しています。
モデルがデフォルトのまま BasicEffect を用いる場合、BasicEffect へ設定するプロパティが XNB に埋め込まれるだけです。このため、読み込んだプロパティを自作クラスへ設定するだけで済みます。カスタム エフェクトを用いる場合、エフェクトのバイトコード、および、エフェクトに設定するプロパティがディクショナリ形式で埋め込まれます。それらの読み込みは簡単ですが、データの利用には汎用的なエフェクトの枠組みが必要であろうかと思います。XNB はシェーダ モデル 3.0 以下だと思うので、その考慮も大変そうです。
これで、FBX ファイルの読み込みと最適化を XNA コンテンツ プロジェクトに任せてしまい、自分は XNB を利用するだけ、という事が可能になったので、テスト コードやデバッグ コードの記述が随分と楽になるんじゃないかなぁと。
ただ、こうなると、「SpriteFont もコンテンツ プロジェクトで管理して XNB から読み込めばいいんじゃないの?」・・・みたいな。自分で専用コードを実装してしまいましたが、破棄しようかなぁ~と。
公式サイトの XNB フォーマットの説明にコード例(C++)も含まれているので、読み込みに関する実装は簡単でしたが、自分が作成した Texture2D クラスのバグと言うか、自分の知識不足のために難儀しました・・・。
ただ、読み込めるデータには制約を設け、例えば、モデルで扱えるエフェクトを BasicEffect に限定したりなどしています。また、今はモデルを読み込みたいだけなので、その他は無視しています。
モデルがデフォルトのまま BasicEffect を用いる場合、BasicEffect へ設定するプロパティが XNB に埋め込まれるだけです。このため、読み込んだプロパティを自作クラスへ設定するだけで済みます。カスタム エフェクトを用いる場合、エフェクトのバイトコード、および、エフェクトに設定するプロパティがディクショナリ形式で埋め込まれます。それらの読み込みは簡単ですが、データの利用には汎用的なエフェクトの枠組みが必要であろうかと思います。XNB はシェーダ モデル 3.0 以下だと思うので、その考慮も大変そうです。
これで、FBX ファイルの読み込みと最適化を XNA コンテンツ プロジェクトに任せてしまい、自分は XNB を利用するだけ、という事が可能になったので、テスト コードやデバッグ コードの記述が随分と楽になるんじゃないかなぁと。
ただ、こうなると、「SpriteFont もコンテンツ プロジェクトで管理して XNB から読み込めばいいんじゃないの?」・・・みたいな。自分で専用コードを実装してしまいましたが、破棄しようかなぁ~と。
2013/03/11
SpriteBatch 実装に手こずり中
BasicEffect 風の枠組みの実装はスムーズに完了し、XNA 公式サンプルの Primitives3D を移植し、見た目は上手く動作しているようで、後はさくさくと進められるな!などと思っていたのですが、SpriteBatch でかなり躓いています。
XNA で実装していた時には、SpriteBatch は当たり前の存在とし過ぎていて、内部まで深く考察する事が無かったんですよね。DirectX Tool Kit のソースを見て、なるほどなぁと。
DirectX Tool Kit を参考にひとまず実装したので、サンプル コードとして何が良いかを探している最中です。ちゃんと動くのかなぁ・・・。
XNA で実装していた時には、SpriteBatch は当たり前の存在とし過ぎていて、内部まで深く考察する事が無かったんですよね。DirectX Tool Kit のソースを見て、なるほどなぁと。
DirectX Tool Kit を参考にひとまず実装したので、サンプル コードとして何が良いかを探している最中です。ちゃんと動くのかなぁ・・・。
2013/03/02
SharpDX ラッパーがひとまず稼働
SharpDX のラッパー実装が、SharpDX の MiniCube サンプル アプリケーションの移植まで辿り着き、一応見た目は動いているといった所です。
しかし、まだ頂点シェーダ ステージとピクセル シェーダ ステージの対応のみ、入力処理未対応など、そもそも機能が充実していない事や、見かけ上正常に稼働しているだけでしかない可能性の高さを考えると、更にサンプルを実装しながら修正を加える必要があると感じます。
サンプル アプリケーションとしては、SharpDX のサンプルは簡単な物ばかりであるため、XNA 公式サンプルを試しに移植してみようかと考えています。それにより、実装すべき物が明らかになると思いますし。その前にデバッグ機能を揃えた方が良いのかな・・・。
ある程度落ち着いたら、Minecraft 風に作ったアプリケーションを移植し、動画作成とタグ付けしてのコード公開をあわせて、一段落としないなぁ・・・と。先は長い。
しかし、まだ頂点シェーダ ステージとピクセル シェーダ ステージの対応のみ、入力処理未対応など、そもそも機能が充実していない事や、見かけ上正常に稼働しているだけでしかない可能性の高さを考えると、更にサンプルを実装しながら修正を加える必要があると感じます。
サンプル アプリケーションとしては、SharpDX のサンプルは簡単な物ばかりであるため、XNA 公式サンプルを試しに移植してみようかと考えています。それにより、実装すべき物が明らかになると思いますし。その前にデバッグ機能を揃えた方が良いのかな・・・。
ある程度落ち着いたら、Minecraft 風に作ったアプリケーションを移植し、動画作成とタグ付けしてのコード公開をあわせて、一段落としないなぁ・・・と。先は長い。
2013/02/05
天空光
結局、木の生成はスルーし、天空光・・・と言うのが適切か分からないですが、天空光の処理を実装していました。描画の結果としては、以下のような影を落とすための光の処理です。
基礎的なライティングの問題点
基礎的なライティングとして、拡散色 (diffuse)、放射色 (emissive)、反射色 (specular)、環境光色 (ambient) を定め、シェーダ内で指向性光源 (directional light) の方向から最終的な色を決めるという方法があります。
この方法では、面の法線、光の方向、視点方向のみから色を決定します。つまり、光の到達は考慮されず、光が到達しない面であると我々が認識できる場合であっても光で照らします。
通常、これは大きな問題にはならないと思うのですが、洞窟のような地形の描画において期待しない結果をもたらします。洞窟の奥深くへ行く程、光は到達しなくなることを通常は期待しますが、基礎的なライティングでは、問答無用で洞窟を構成する面を照らしてしまいます。
CPU での天空光シミュレーション
そこで、Minecraft のように、ブロックの各位置への光の到達具合をシミュレートし、テクスチャの明るさを変更するという処理を実装していました。
なお、実装の大枠は自分で考えたのではなく、Terasology (Java) を参考にしています。
前提として、太陽の位置を真上に固定させて考えます。詳しくは知らないですが、Wiki を見る限りでは Minecraft も同じであろうかと思います。太陽の方向を考慮できればベストでしょうが、恐らく、その場合は実用に耐える処理速度を得られない気がします。
大まかな流れとしては、真上からの光の到達具合を判定し、そこから光の拡散を処理するだけです。
第 1 フェーズ
あるチャンクの内部について、Y 軸についてどこまで光が到達するかを判定します。光レベルは 0 から 15 の値を用い、0 が最も暗い状態、15 が最も明るい状態を示しますが、このフェーズでは光レベル 15 で処理します。
第 1 フェーズで定めた光レベル 15 の全ての位置から、上下前後左右に隣接するブロック位置へ、光レベルを減衰させながら、再帰的に拡散させます。減衰しながらの拡散とは、光レベルを -1 しながら、0 となるまで隣接位置へ光レベルを設定していく処理です。ただし、拡散先の位置により大きな光レベルが設定されている場合は、既により強い光で照らされているため拡散を止めます。
第 3 フェーズ
第 2 フェーズまでで、チャンクの内部で閉じた光レベルの情報が構築されるので、次に互いに隣接するチャンク同士の光の拡散を処理します。これには、あるチャンクについて、隣接チャンクの外周の光レベルを参照して自チャンクの外周の光レベルを更新し、ここから再度、光の拡散を自チャンク内部で行います。これにより、チャンクの光レベルの設定が全て完了するため、メッシュの更新を要求し、メッシュの頂点に光レベルに基づいた陰影情報を書き込むようにします。
スクリーン ショット
天空光を処理することで、以下のスクリーン ショットのように、洞窟の雰囲気を出せるようになります。
明るくなっている部分は洞窟の入り口であり、そこから奥へ向かうにつれ光が届かなくなり、ブロックは黒くなります。
とまぁ、こんな事をしていました。ただ、非同期処理とは言え、随分と CPU が苦しそうにしているので、もう少しコードを改善しなければならないと感じています。
基礎的なライティングの問題点
基礎的なライティングとして、拡散色 (diffuse)、放射色 (emissive)、反射色 (specular)、環境光色 (ambient) を定め、シェーダ内で指向性光源 (directional light) の方向から最終的な色を決めるという方法があります。
この方法では、面の法線、光の方向、視点方向のみから色を決定します。つまり、光の到達は考慮されず、光が到達しない面であると我々が認識できる場合であっても光で照らします。
通常、これは大きな問題にはならないと思うのですが、洞窟のような地形の描画において期待しない結果をもたらします。洞窟の奥深くへ行く程、光は到達しなくなることを通常は期待しますが、基礎的なライティングでは、問答無用で洞窟を構成する面を照らしてしまいます。
CPU での天空光シミュレーション
そこで、Minecraft のように、ブロックの各位置への光の到達具合をシミュレートし、テクスチャの明るさを変更するという処理を実装していました。
なお、実装の大枠は自分で考えたのではなく、Terasology (Java) を参考にしています。
Terasology
http://blog.movingblocks.net/blockmania/
他にも幾つか Minecraft クローンのオープン ソースを調べたりしましたが、Terasology のコードが最も充実している、かつ、可読性が高いと感じます。処理の概念は Terasology と大差ないと思いますが、Minecraft や Terasology とは異なり、僕の実装では上下方向についてもチャンクを無限に広げているため、若干、シミュレーションでの判定ロジックが異なります。また、それらとは元より方向性が異なることもあり、シミュレーション結果を得る前にメッシュを構築して描画してしまい、シミュレーション完了でメッシュを再構築するなどしています。
ゲームとして考える場合は、シミュレーション完了を待ってメッシュ構築すべきかと思います。処理の流れ
前提として、太陽の位置を真上に固定させて考えます。詳しくは知らないですが、Wiki を見る限りでは Minecraft も同じであろうかと思います。太陽の方向を考慮できればベストでしょうが、恐らく、その場合は実用に耐える処理速度を得られない気がします。
大まかな流れとしては、真上からの光の到達具合を判定し、そこから光の拡散を処理するだけです。
このような処理であるため、グリッドに沿った天空光の処理なのかなと思っています。なお、非同期処理が前提です。ここに限らず、既にほとんどの処理が非同期ですが。
第 1 フェーズ
あるチャンクの内部について、Y 軸についてどこまで光が到達するかを判定します。光レベルは 0 から 15 の値を用い、0 が最も暗い状態、15 が最も明るい状態を示しますが、このフェーズでは光レベル 15 で処理します。
15 は、すなわち 4 ビットです。つまり、byte で 2 つの位置を管理でき、メモリ節約の恩恵を得られます。第 2 フェーズ
第 1 フェーズで定めた光レベル 15 の全ての位置から、上下前後左右に隣接するブロック位置へ、光レベルを減衰させながら、再帰的に拡散させます。減衰しながらの拡散とは、光レベルを -1 しながら、0 となるまで隣接位置へ光レベルを設定していく処理です。ただし、拡散先の位置により大きな光レベルが設定されている場合は、既により強い光で照らされているため拡散を止めます。
第 3 フェーズ
第 2 フェーズまでで、チャンクの内部で閉じた光レベルの情報が構築されるので、次に互いに隣接するチャンク同士の光の拡散を処理します。これには、あるチャンクについて、隣接チャンクの外周の光レベルを参照して自チャンクの外周の光レベルを更新し、ここから再度、光の拡散を自チャンク内部で行います。これにより、チャンクの光レベルの設定が全て完了するため、メッシュの更新を要求し、メッシュの頂点に光レベルに基づいた陰影情報を書き込むようにします。
この部分は Terasology とは異なる実装にしていると言うか、Terasology のロジックを理解できなかったので、極普通の思考のまま実装しています。もしかすると、演算回数を減らす工夫が Terasology にはあるのかもしれません。
スクリーン ショット
天空光を処理することで、以下のスクリーン ショットのように、洞窟の雰囲気を出せるようになります。
明るくなっている部分は洞窟の入り口であり、そこから奥へ向かうにつれ光が届かなくなり、ブロックは黒くなります。
実際には、環境光遮蔽 (ambient occlustion) を別途シミュレートしているため、ブロック同士で遮蔽する部分は、更に黒くなっています。以下のスクリーン ショットのように、強引に地形の内部へカメラを置けば、完全に閉じている洞窟などでは、完全に影で覆われてしまう状態であったりします。
とまぁ、こんな事をしていました。ただ、非同期処理とは言え、随分と CPU が苦しそうにしているので、もう少しコードを改善しなければならないと感じています。
登録:
投稿 (Atom)
libgdx いじり
Google が提供している Java 版の Tango Examples は Rajawali をベースにしているため、自分が仕事で開発する Tango アプリも Rajawali ベースとしていましたが、最近は libGDX への移行を進めています。一応、要点については移行が...
-
ゲイジ、マヤ(ボス前放置)、クリーグと三周目本編ソロを終え、今は、初見時に選んだアクストンで三周目保護区ソロを終えた所です。初見時は特に強いキャラではないと思っていたのですが、改めて操作すると異常に強いですねぇ・・・(タレットが)。ブラッドウィング戦もヌルゲーと化していました。 ...
-
アクストンも三周目本編を終えました。今まで使ったキャラと比べると、飛び抜けて楽でしたねぇ・・・。 ビルドは、スラグを撒くのがダルいのでゲリラ ツリーから ダブル アップ まで、チキン野朗として戦いたいのでガンパウダー ツリーから ロングボウ タレット まで、サバイバル ツリー...
-
XNA へ移植していた Continuous Distance-Dependent Level of Detail (CDLOD) の実装を概ね終えました。 CDLOD: Filip Strugar: Oh no, another terrain rendering pape...























