Twitter @HigenekoTwitter #XNA
テクスチャが欲しい
その昔、コンシューマ機で3Dが使えるようになった時に使えるテクスチャのサイズは非常に限られたもので、中には最大32x64という、ウェブサイト上に使われるバナーより小さいサイズのテクスチャしか使えないマシンもありました。それが今ではSM1.1が使えるビデオカードでは2,048x2,048が基本になり、Xbox 360やDX10が使えるビデオカードでは8,192x8,192までの大きさのテクスチャが使えるようになりました。
これだけ大きなサイズのテクスチャが使えると、より高精度なものを作ることができる利点はありますが、逆に言えばそれだけのクオリティのものを作らなければいけないという面倒さが増えてしまいました。
さて、これだけのクオリティのテクスチャを用意するにはどうしたら良いでしょうか?
ネットからダウンロードできるテクスチャは、サイズが小さく、jpg等の圧縮が掛かっていて画質が劣化していたり、法的問題もあるので、思い通りのテクスチャを探すのは意外と大変です。
次に、自分でテクスチャを描く方法ですが、第一にサイズが大きいので細かいディテールを描くのが大変なのと、普通の絵を描くときには光と影の表現を考えながら描きますが、最近の3Dゲームではポリゴン単位での陰影はもちろん、ノーマルマップを使って細かい陰影や、影などもリアルタイムに処理できるようになり、手で描いた絵に陰影があると計算された陰影や影と食い違いがあるので、そういった処理がされた後にどう見えるかを考えながら描かないといけません。絵を描くのが得意な人ほどちゃんとしたテクスチャを作るのに苦労するなんて話をよく耳にします。
3のデジカメですが、最近のデジカメでは3,264x2,448のサイズの写真を撮ることのできる800万画素が主流になり、1,000万画素を超えるものも珍しく無くなってきました。これだけの解像度があれば2,048x2,048のテクスチャを作るのには十分です。テクスチャはモデルの表面の質感を出すためのものなので、被写体の姿や形に関係ないものであっても素材が近いものであれば十分です。大理石でできた神殿を作るのにわざわざギリシャあたりまで行かなくとも、近所の学校にある記念碑の磨かれた表面部分を床のテクスチャ、荒い裏側の部分は柱のテクスチャとして使ったり、近所で寝ている猫の背中部分を撮ってトラっぽいモデルに使ってみたりと、近所を散歩するだけでオリジナルの色々な素材が簡単に入手することができます。
カメラ片手に散歩気分でテクスチャ集め
テクスチャの素材を写真で撮るときの注意としては
1の解像度は常に最高にして、使えるのならRAWフォーマットを使います。RAWフォーマットの場合16ビットRGBに変換することができるので、後からディテールを損なうことなく明るさの設定ができるので便利です。ISOの値が高いとノイズが発生するのでなるべく低い数値で。
2は、直射日光が当たると影が強くでてしまうので前述のように処理した陰影との食い違いがでてしまいます。
3については、ここで撮るのはあくまでテクスチャとしてのものなので、できるだけ最終的に使うテクスチャの形に近い形で撮るのが理想的です。レタッチソフトなどでは撮ったイメージを三次元的に加工できるものがありますが、撮る時点で気を配ることで後の作業に掛かる手間を減らすことができます。
撮った写真をテクスチャに加工する
素材として撮った写真は殆どの場合、そのままではテクスチャとしては使えません。通常、テクスチャはタイルのように並べて貼り付けることが多いので、撮った写真をそのまま使うと繋ぎ目が見えてしまいます。
そこで、フォトショップやGIMPといったツールを使って、その繋ぎ目を無くします。フォトショップの場合はここにある「繋ぎ目のないテクスチャ」で紹介されているように、スクロールやスタンプツールを使います。GIMPの場合はResynthesizerやTexturizeといったプラグインを使うことでできます。
左上の写真は会社の前にある芝生を撮った写真を正方形にしたものです。そして、右側はフォトショップのフィルタ/その他/スクロールを使って画像の解像度の半分ずらしたものです。
スタンプツール等を使って繋ぎ目を消すと上の様になります。で、このテクスチャを実際にゲーム内に使うと……
繋ぎ目の無いテクスチャにはなり、自分で撮った写真が無事にテクスチャとして使えるようになりました。では、皆さんデジカメ使って自��なりのテクスチャを作りましょう……。
???
何かおかしい。確かに繋ぎ目は無くなりましたが、同じパターンのテクスチャが並んでいるというのがハッキリと判ると思います。
低周波問題
結論から言うと、撮った写真の繋ぎ目を消しただけでは、その画像に含まれる低周波成分がそのまま残っているというのが問題です。画像が波で表すことができるといった細かい説明は他の機会にしますが、ここで言う低周波成分とはテクスチャ全体にわたって緩やかに変化する陰影のことです。今回の例では明るい部分の芝生と暗い部分の芝生の色があることです。他にも同じ色だと思って撮った壁が照明の関係で微妙にその色が変化したり、カメラによっては口径食(Vignetting)やケラレと呼ばれる撮った写真の四隅が微妙に暗くなるといった変化も低周波成分に含まれます。低周波があれば高周波もあるわけですが、ここでは草一本一本の細かなディテール部分が高周波成分に相当します。
この低周波成分を画像からグラデーションツール使ったりして手動で取り除くのは非常に面倒な作業です。綺麗になったと思っても、まだ微妙に残ってたりして何度もやり直しをしなければいけません。
ハイパスフィルタ
この低周波問題を解決するのに便利なフィルタがフォトショップのフィルタメニューの奥地(フィルタ/その他/ハイパス)に潜んでいるハイパスフィルタ(high-pass filter)です。GIMPにもハイパスフィルタのプラグインがあります。良く高周波成分を取り除くローパスフィルタは耳にしますが、このハイパスフィルタはその逆に低周波成分を取り除くフィルタです。
左上の図はハイパスフィルタを使っている時の様子です。適用半径を好みの設定にします。この時、芝生の緑色の変化も低周波と見なされモノトーン風になってしまうので、その後に右上の様にカラーバランスを使って元の色に戻します。
上はカラーバランス調整後に最終的に繋ぎ目を取る為にスクロールさせたものです。すでにパッと見では繋ぎ目は見えませんが拡大表示すると見えてくる繋ぎ目をスタンプツールなどを使って消します。
左上は繋ぎ目を消して完成したテクスチャです。右上はハイパスフィルタを使わずに繋ぎ目を消したテクスチャです。
で、このテクスチャを実際にゲーム上で使ってみると……
上の様になります。目を凝らさない限りは同じテクスチャが並べてあるようには見えない綺麗な芝生ができました。
ただ、今度は低周波成分が皆無に等しいので無機的な感じがします。今回は完全平面な地面に張ったテクスチャなので余計にそう感じますが、実際のゲームでは多少の起伏があるので微妙な陰影がつくことで無機的な感じは少なくなります。また、より自然に見せるためにマルチテクスチャを使った方法もありますが、その紹介は他の機会にします。
まとめ
今回はプログラムからょっと離れてテクスチャ素材の作り方を紹介しました。テクスチャというと、私もそうですが絵が描けない人にとってはあまり触れたくないない話題でもありますが、今回紹介した方法ならデジカメとちょっとしたツールの使い方を覚えるだけで綺麗なテクスチャ素材を簡単に作ることができます。
プログラムに疲れたら、気分転換の散歩ついでにオリジナルなテクスチャ素材を集めてみてはどうでしょうか?
Xbox 360でゲーム開発をしているときに聞く単語のひとつにTCR(Technical Certification Requirements)というものがあります。コンシューマーゲーム機用のゲームを出すためには、このTCRで決められた要求を満たす必要があります。例えば「コントローラーの接続が切れたら、ゲームをポーズ状態にしてユーザーに対してコントローラーの再接続を促すメッセージを表示する」と、いった感じです。プロジェクトの追い込み時期になるとバグデータベースにある直さないといけないバグのリストでよく目にする単語だったりするので、良い思い出がない開発者もいると思います。
全てのTCRを満たすのは面倒な作業ではありますが、その殆どがプレイしてくれるユーザーが快適にゲームをプレイする為には必要不可欠なものばかりです。そこで、今回は数あるTCRの中からXNA GS上でゲームを作るときにも役に立つTCRをいくつか紹介します。
通称5秒ルール。例えばタイトル画面を「Push Start」と描きこまれた静止画一枚を表示したりするとアウトです。要するに常に画面のどこかが動いていることで、ユーザーにゲームが固まってしまった状態ではないと認識させるものです。
簡単な解決方法としては、Push Startといった文字は別に表示してカラーアニメーションさせたり、点滅させたりする方法があります。3Dモデルを表示できるのなら、3Dモデルをゆっくりと回転させるといった手もあります。
ゲームが固まった状態と区別できるという実用的な目的のほかに、常に画面上にアニメーションする部分があると、していないものに比べると楽しく見えるという効果もあるので、ゲーム画面のデザインをするときにアニメーションも考慮することをお勧めします。
「ユーザーが操作できない状態が15秒以上続く場合は、ユーザーを飽きさせない工夫をする」
これはロード画面のことで、Xbox 360のゲームでは「Now Loading...」という単に文字を表示させるだけではなく、ゲームの進行に関する情報や、ゲームプレイ時のTIPSなどを表示しているのは、この要求を満たすためです。以前は単に文字列を表示するという単純なものが大半をしめていましたが、最近では次のミッションの説明をしている間に裏でロードしてるというゲームも見られるようになりました。
ユーザを飽きさせないというのはゲームを作る上で非常に大事なことなので、常にユーザーを楽しませるように努力しましょうということを奨励しているTCRです。
PC用のモニタと違って、TVモニタの場合は画面の上下左右が切れてしまうことがあります。ですから、そのことを考慮せずに画面端にゲームにHPゲージやスコアといったものを表示するとTVによっては見えなくなることがあります。
この問題を避けるためにゲーム中に表示されるメッセージなどは、解像度の90%のセーフエリアといわれる領域に表示するようにします。1280x720の場合はスクリーン座標の(64、36)から(1216、684)の152x648の大きさの部分にメッセージを描くようにします。特に重要度の高いメッセージは解像度の80%の部分に表示するように決められています。
と、数字で書いても判らないので、例として1280x720の時のセーフエリアを表示したイメージ(クリックするとオリジナルサイズで表示します)を作ってみました。上の図の明るい部分がセーフエリアになっています。
ただし、同じセーフエリアをPCモニタ上でも適応すると今度は周りに空白が目立つようになるので、その空白が気にならないデザインにするか、セーフエリアの矩形情報を持っておき、Xbox 360上では解像度の90%の大きさにし、PC上では解像度と同じ大きさにするようにして「セーフエリアの右端からxピクセルの位置」といった感じに指定できるようにする方法があります。
Xbox 360のeDRAMは4xMSAA使用時にピーク性能を発揮できるように設計されていて、少ない容量のeDRAMに収まらないサイズでも負荷を抑えながら分割してレンダリングするタイリングをサポートしています。せっかくある機能を使って欲しいのでアンチエリアスを使うことが奨励されています。
PCの場合でもXNAを動かすのに必要なシェーダーモデル1.1が使える殆どのビデオカードにはアンチエリアス機能があり、特に最近のビデオカードでは4xMSAA以上のアンチエリアスを使っても速度的なペナルティが少ないものが多くなってきました。また、XNA GS上ではタイリングを気にせずに大きなサイズのレンダーターゲットを指定できるようになっています。
左上の図は4xMSAAありで、右上がAAなしの時のスクリーンショット(クリックで拡大表示)です。
上の図は等倍の比較図です。静止画でも違いが判ると思いますが、実際に動かしてみるとAAなしの場合はポリゴンのエッジ部分にでるちらつきが4xMSAAでは殆どありません。
実際の使い方ですが、以下のPreparingDeviceSettingイベントにフックするコードをゲームクラスのコンストラクタ部分に追加します。
public Game() { graphics = new GraphicsDeviceManager(this); graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings); }
そして、以下のコードを追加するとXbox 360、PCの両方でアンチエリアスが使えるようになります。PCの方はアンチエリアスをサポートしていないビデオカードもあるので、CheckDeviceMultiSampleTypeを使ってアンチエリアスが使用可能かのチェックをしています。
void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e) { PresentationParameters pp = e.GraphicsDeviceInformation.PresentationParameters; // 4xAAと2xAAが使えるなら使っちゃう GraphicsAdapter adapter = e.GraphicsDeviceInformation.Adapter; SurfaceFormat format = adapter.CurrentDisplayMode.Format; // 使いたいAAのタイプを順に使えるか調べる MultiSampleType[] multiSampleTypes = { MultiSampleType.FourSamples, MultiSampleType.TwoSamples }; foreach (MultiSampleType mst in multiSampleTypes ) { int quality = 0; if (adapter.CheckDeviceMultiSampleType(DeviceType.Hardware, format, false, mst, out quality)) { // 最初に見つけた使用可能なAAを設定する。 pp.MultiSampleQuality = 0; pp.MultiSampleType = mst; break; } } }
アンチエリアスがビデオカードで使用できるようになった頃はアンチエリアスを使ったときの速度低下が大きな問題でしたが、前述のように今時のGPU上でのアンチエリアス使用時の負荷は少なく、Xbox 360上では4xMSAA使用時の負荷は使用していないときに比べると20%程の増加になります。特にGPUがヒマになりがちなXNAフレームワーク上で動いてるゲームでは常に使用していても速度的な変化は殆どないので、最初に4xMSAAを使うようにして、GPUの負荷が多くなったときに2xMSAA、AAなしと変えるようにしてみてはどうでしょうか?