上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
- --/--/--(--) --:--:--|
- スポンサー広告
-
-
この記事は
UE4 Advent Calendar 2016の15日目の記事です。
UE4ではポストプロセスの作成にポストプロセスマテリアルを使用します。
通常のマテリアルと同様にマテリアルエディタで作成でき、マテリアルインスタンスも使用できます。
ポストプロセスのパラメータをランタイムで変更する場合はパラメータコレクションを利用する必要がありますが、BPを利用すればレベルごとの設定などもそれほど難しくなく便利です。
しかしいくつかの問題点が存在します。
そのうちの1つで、個人的に最も大きいのがオフスクリーンバッファを使用できないことです。
ポストプロセスを本格的に作成する場合、複数の画像をコンポジットする必要が出てきます。
ポストプロセスマテリアルでもG-Bufferとして保存されている画像はコンポジットすることが出来ますが、それらから生成した複数の画像をコンポジットするということが出来ません。
なのでポストプロセスマテリアル内でそれらすべての画像の加工からコンポジットまで行う必要があります。
それで実装可能であれば問題ないのでは?と思う方もおられるかもしれませんが、加工が重い処理の場合は速度的にどうなの?とか、クオリティは保てるの?などの疑問も出てきます。
それらを解決する方法としてエンジン側にポストプロセスを作成、埋め込むという方法があります。
もちろんエンジン改造となるので改造コストは安くないですし、特に改造後のエンジンバージョンアップに対応するコストが馬鹿になりません。
ただ、ポストプロセス部分は割と独立してる感のある部分なので、既存のポストプロセスの順番を入れ替えるよりマージコストは低いのではないかと思います。
というわけで、今回は以前ポストプロセスマテリアルで実装したディフュージョンフィルタを、エンジン改造によってよりクオリティの高いものにする実験を行いました。
ディフュージョンフィルタは以下のようなパスで実装されます。

元の画像の色を2乗して、このバッファをガウスブラー、2乗カラー画像とブラー画像をスクリーンブレンドし、最後に元画像との間で色の最大値を取ります。
ポストプロセスマテリアルの実装ではカラーの2乗~比較(明)までの処理が1パスで処理されていました。
問題点を上げるとするなら、ガウスブラーを行う前にかならずサンプリングしたテクセルを2乗する必要があるという点です。
2乗した画像が別に存在するなら、ただそれをガウスブラーすればいいだけなのですが、ポストプロセスマテリアルでは1パスですべての処理を終わらせなければならないのでこのような実装になります。
また、ガウスブラーの実装にも問題があります。
ガウスブラーのGPU実装はX軸方向のガウスフィルタとY軸方向のガウスフィルタを別パスで、つまり2パスで行うことが多いです。
この方が高速で、且つ綺麗にブラー出来ます。
ポストプロセスマテリアルではこの手法は使えないので、ボックスブラーよろしくボックス状にテクセルをサンプリングしてガウスブラーを掛けなければなりません。
綺麗にしようと思うとサンプリング数がかなり増加して速度に問題が発生します。
最後の問題点は縮小バッファが使えないことです。
ガウスブラーは画面をぼかしてしまうので、低解像度でもさほど問題ない場合があります。
カラーの2乗を行う際に縮小バッファに描画し、これをガウスブラーするのであればより高速ですが、ポストプロセスマテリアルではそのようなことが不可能です。
今回の実装はボカシの綺麗さだけではなく、速度的にも解像度によっては有利です。
こちらは比較的高解像度での処理速度比較です。

DiffusionPow2~Diffusionまでが今回入れた処理です。
2乗カラーをガウスブラーしてダウンサンプルしてガウスブラー…という感じでガウスブラーを3回行った結果をぼかし画像として使用しています。
合計が0.57msでその下にあるポストプロセスマテリアルでの実装より少しだけ高速です。
ただし、解像度が低い場合はポストプロセスマテリアル実装のほうが高速です。
もちろん、クオリティはエンジン改造のほうが高いですね。
というわけで、続きからこの実装方法について書いていきます。
なお、.usf, .h, .cppファイルをそれぞれ1つずつ追加している点にご注意ください。
追加してプロジェクトに登録する方法は書いていません。
[[UE4] エンジンを拡張してポストプロセスを実装する PS編]の続きを読む
- 2016/12/15(木) 00:04:29|
- UE4
-
| トラックバック:0
-
| コメント:1
ちょっと小ネタ。
Visual Studioには標準でグラフィクス診断という機能が備わっています。
その昔、DirectX SDKにはPixというツールが存在していました。
このツールはDirectXの描画が何をやっているか、どれくらい時間がかかっているかなどを調べるのに大変重宝するものでした。
現在、DirectX SDKはVisual Studioに統合されており、Pixもなくなってしまいました。
その代わりにVisual Studioが標準で診断機能を追加しているというわけです。
グラフィクス診断そのものの使い方はここでは述べませんが、Visual Studio 2015でグラフィクス診断をするには、C++プロジェクトをビルド後、[デバッグ] → [グラフィックス] → [グラフィックス デバッグの開始] を選択します。
通常はこれでグラフィクス診断が起動し、PrintScreenボタンでそのフレームの診断が可能です。
しかし、いつのバージョンからかはわかりませんが、UE4ではグラフィクス診断を起動すると以下のウィンドウが表示されてアプリが停止します。
これはデバイス作成時の設定が原因となっているようで、現状ではこのままグラフィクス診断を使用できません。
使用するにはエンジンコードを書き換える必要があります。
書き換え自体は非常に簡単で、以下のように修正します。
Engine/Source/Runtime/Windows/D3D11RHI/Private/Windows/WindowsD3D11Device.cpp (338)
// Add special check to support HMDs, which do not have associated outputs. // To reject the software emulation, unless the cvar wants it. // https://msdn.microsoft.com/en-us/library/windows/desktop/bb205075(v=vs.85).aspx#WARP_new_for_Win8 // Before we tested for no output devices but that failed where a laptop had a Intel (with output) and NVidia (with no output) //const bool bSkipHmdGraphicsAdapter = bIsMicrosoft && CVarExplicitAdapterValue < 0 && !bUseHmdGraphicsAdapter; const bool bSkipHmdGraphicsAdapter = false;
|
この bSkipHmdGraphicsAdapter が false になっていればOKです。
これをビルドして、ここからC++プロジェクトを作ればグラフィクス診断が行なえます。
なお、この修正方法はUE4.14にて有効な手法です。
他のバージョンでは少々異なるかもしれませんが、大抵はこのソースファイルを調べれば対応できるでしょう。
RenderDocを使うというのも1つの手段ですが、これはどうもプラグインを入れる必要があるので、プラグインなしでグラフィクスの調査をしたい場合はこの書き換えを行った上で診断してみましょう。
- 2016/11/23(水) 23:38:55|
- UE4
-
| トラックバック:0
-
| コメント:0
TL上で少し話題になったPBR対応テクスチャがSubstanceDesignerで作れるかどうかという話。
実はPBR対応のテクスチャを作成するためのユーティリティがSDにはいくつか存在しています。
今回はそれらのノードの話を。
PBRに対応するとは?そもそもPBR対応というのは、例えば石なら石として正しい色になっている、ということではありません。
石と言っても種類は色々ありますし、同種の石でも成分に違いは出ます。
同じ花崗岩でも瑞牆と小川山では色味の違いは出ますし、表面の凹凸も変わってきます。同じ山でも場所が違えば違ってきます。
どうしても瑞牆の花崗岩を再現したい!というのであれば、瑞牆に行って岩の写真を撮影するしかありません。
ではPBR的に正しいテクスチャとは何かというと、アルベドであれば黒の下限と白の上限です。
RGBすべての成分が0、RGBすべての成分が1という黒と白は現実には存在しません。
前者はすべての光を完全に吸収するのでブラックホール化何かでしょう。
後者は拡散反射なのに光を完全に反射します。ホワイトホール?いや、まあ、普通にないですね。
また、金属であれば純金属はその反射率がすでに求められています。
純度100%でなければ変化はするかもしれませんが、基本的には求められている反射率で問題ないはずです。
つまり、PBR的に正しいということと、特定の物体(瑞牆の阿修羅岩とか)の色が正しいというのは別の問題だということです。
特定物体の完全に正しい色がほしい、というのであればフォトスキャンするしかありませんが、そこまで特定の何かがないのであれば物理的に間違ってない値を使って目合わせでもいいんじゃないかな、と思っています。
もしも特定物体の正しい色がほしいのだ!という方はポリフォニーの内村氏が今年のCEDECで解説してた色彩工学の資料をお読みください。
http://d65.xyz/cedec2016では、各ノードを解説していきます。
これらのノードは[Material Filters]->[PBR Utilities]の中にあります。
BaseColor/Metallic/Roughness converter
SDでは通常、BaseColor/Metallic/Roughness のパラメータでマテリアルを作成します。
しかしそれとは別に Diffuse/Specular/Glossiness のパラメータでマテリアルを作成するシステムも有ります。
このノードは通常の BaseColor/Metallic/Roughness で作成されたマテリアルを別のマテリアル表現に変換してくれます。
どのような種類があるかというとこんな感じ。

Diffuse…以外にも、VrayやArnoldといったレンダラにも対応しています。
あまり使う機会はないかもしれませんが、今までBaseColor方式で作っていたマテリアルを急遽別のシステムに出さなきゃいけないという場合には重宝するかもです。
PBR Albedo Safe Color
上でも書きましたが、PBRにおいてアルベドの黒と白は完全な黒と白にはなりません。
このノードはそのような黒や白を妥当は範囲にクランプしてくれるノードです。
例えば全て0の真っ黒を繋げるとこうなります。

グレーっぽくなっているのがわかりますよね。
なお、入力ピンの下の方はMetallicです。完全金属の場合はBaseColorは素通りされます。
そのような場合は本来何を入力しても真っ暗にしたほうが良かったのでは?と思ったり思わなかったり。
パラメータでDiffuseモデルにも変更ができます。
また、Torleranceパラメータで許容誤差を変動させることが出来ます。
このパラメータが0なら50 sRGBに、逆に1なら誤差とを大きく持って30 sRGBくらいまではクリッピングされません。
まあ、非金属のマテリアルを作って、その際にBaseColorに自身が持てないような場所に使う機能という感じでしょうかね。
PBR BaseColor/Metallic Validate
このノードも前者と同じようなノードですが、こちらはBaseColorをクリップしたりしません。
このノードの場合はBaseColorにミスがある部分が赤くなります。PBR的に正しい場合は緑です。

[Level]ノードでレベルを変更した結果として溝の部分が白飛びしています。
これがPBRのミスです。
この結果を利用して赤くなっている部分のベースカラーをマスキングすることも可能です。
目合わせでマテリアルを作っている場合は最後にこのノードを通して問題ないかだけは調べておく方がいいと思います。
PBR Dielectric F0
非金属物体の一部を選択してフレネル反射係数 f0 を取得します。
f0が求まるとIORなんかも求められますが、[Custom IOR] を利用すればIORから係数を求めることが出来ます。
何に使うの?と言われると、まあ、ちょっと困るねw
選択できる物体はこんな感じです。
PBR Metal Reflectance
最後はこちら。金属の反射率を金属の種類に応じて取得できるノードです。
今まではDONTNODのチャートを利用していたのですが、こちらを使ったほうが簡単でしょう。
何が選べるかは以下のようになっています。

代表的な金属は網羅されているかと。
というわけで今回はここまで。
- 2016/09/02(金) 02:06:12|
- Substance Designer
-
| トラックバック:0
-
| コメント:0
今回紹介するのは入力画像の特定条件にあるピクセルにのみ指定パターンを表示する方法です。
[Tile Sampler] ノードにはランダムに配置したパターンがグレースケールの白の部分にのみ表示されるようにする機能があります。
この記事ではその簡易バージョンを作る方法を提示します。
簡易バージョンなのでもう少しちゃんと作らないと実用的にはならないのですが、仕組みがわかっていれば [Tile Sampler] ノードから該当部分を抜き出すこともできるようになるんじゃないでしょうか?
まだ自分はできていませんが、そのうちやる予定。
この処理には [FX-Map] を使用します。
このノードはSDでは唯一ループ処理を行うことが出来るノードであり、今回のようにパターンを複数回散りばめるのには大変便利です。
特定パターンを複数表示するノードとしては先に紹介した [Tile Sampler] も含まれるタイル系やスプラッタ系がありますが、これらは内部で [FX-Map] を使用しています。
まずは準備をしましょう。以下のようにノードを作成します。

[Image Input 0] に入力された円が今回は散りばめるパターン、[Image Input 1]に入力された三角が表示位置を示す画像とします。
[FX-Map] ノードの [Color Mode] はグレースケールに変更しておきましょう。
次に入力パラメータを追加しておきます。以下のように作成します。

[tile_num_x/y] はXY座標のタイルの数で、この数値分画像が分割されます。
[gray_threshold] は [Image Input 1] の該当ピクセルがこの値以上の場合にパターンを表示するようにするためのしきい値です。
では、[FX-Map] を選択し、[Ctrl + E] で内部に入っていきましょう。
内部は以下のようにします。

最初から入っている [Quadrant] ノードの上に [Iterate] ノードを配置し、これをルートとします。
[Iterate] ノードがループ処理を行うノードで、このノードの [Iterations] パラメータで指定した回数だけ [Quadrant] ノードが実行されます。
[Iterate] ノードを複数繋げて多重ループも実現できるのですが、繰り返し回数を変数として取得できるのはその変数を使用したノードより手前直近の[Iterate]ノードのみです。
つまり、多重ループのそれぞれのループ回数は取得できません。
今回はそれでは困るので、1回のループで2重ループと同じ効果を出すようにします。
[Iterations] パラメータを空の関数とし、内部を以下のように作成します。

タイルのXYの整数を乗算して出力します。
例えばX=2、Y=2であれば4回ループとなります。タイル数分だけループするわけですね。
次に [Quadrant] ノードの処理を行います。
パラメータを変更するのは [Pattern] を [Input Image] に変更するだけです。
あとは各種パラメータの関数内部で処理を行うことにします。
まずはタイル上にパターンを配置しなければなりませんので、その処理を作成します。
[Branch Offset] のパラメータを関数化し、内部に入りましょう。
この関数は以下のように実装します。

(クリックで拡大)
ちょっと複雑でわかりにくいかもしれません。
まず左下のブロックはタイル1つ分の幅と高さを求めています。
タイルの分割数がXYともに2ならここで求められる値は(1/2, 1/2)=(0.5, 0.5)となります。
右下のブロックはパターンを左上ピッタリに配置する場合のオフセット値を求めています。
最後に上のブロックで繰り返し回数からXYのタイル座標を求め、それに見合った左上からのオフセット値を求めます。
これを右下のブロック(つまり左上のオフセット座標)に加算することで、繰り返し回数に依存したパターンのオフセット値を求めることが出来ます。
しかしこれだけでは大きなパターンが移動するだけです。サイズも適切なサイズに変更する必要があります。
この処理を [Pattern Size] パラメータ内に作成してもよいのですが、実はパターンサイズとして利用するパラメータは左下ブロックの結果と同等だったりします。
同じ計算を何度もやるのは面倒です。なので、この結果をローカル変数に設定します。
上の図の真ん中に [Set] ノードがあります。これは指定名(ここでは [pattern_size])のローカル変数を作成し、そこに値を格納しています。
この [Set] ノードで設定した値を使用するにはいくつかの条件が存在します。
1つ目の条件は、この値はこの関数を持ったパラメータが所属しているノード内でのみ有効です。
つまり、この場合は [Quadrant] ノードでのみ使用可能です。
使用する場合は [Get ~] ノードを使用しますが、同一関数内以外ではドロップダウンリストに表示されないのでて入力する必要があります。
2つ目の条件は [Set] ノードを利用したパラメータ以下のパラメータの関数内でしか使用できません。
今回の場合は [Branch Offset] 内部で使用しているため、この関数内部か、そこから下の [Quadrant] ノードのパラメータでしか利用できません。
つまり、[Color / Luminosity] パラメータの関数内では使用できないということです。
3つ目の条件としては [Set] ノードを有効にするには出力ノードとして設定したノードに対する処理の途中で挟む必要があるということです。
これについては後で説明します。
さて、ここで [pattern_size] というローカル変数にパターンサイズとして利用できる値が入力されましたので、[Pattern Size] パラメータの関数内でこれを使用しましょう。
[Pattern Size] パラメータを関数として、内部を以下のように設定します。

ここまで処理を作成すれば綺麗なタイル状に円が敷き詰められるはずです。
しかし、これからが本番。[Input Image 1] のイメージに従ってしきい値以上のグレーならパターンを表示するようにしましょう。
これを実現するにはタイル状に敷き詰められた各パターンの中心のピクセルを [Input Image 1] から取得し、この値がしきい値以上かどうかを確認、しきい値未満であれば [Pattern Size] を0にするという手法を取ります。
そのような加工をした結果が以下となります。

(クリックで拡大)
右上の部分がパターン中心のイメージのピクセルをサンプリングし、これがしきい値以上かどうかを調べている部分です。
しきい値以上であれば加工前の [pattern_size] 出力を選択し、しきい値未満であれば (0, 0) を選択します。
この選択結果を [pattern_size] に入力していますが、その後に [Sequence] ノードに繋げるようにしています。
なぜこのようにするのかというと、これまで通りに下部の加算部分を出力ノードとして選択してしまうと、その結果を求めるのに [pattern_size] への入力は通る必要がなく、SD内部で不要なノードとして無視されてしまうのです。
関数の出力には影響を与えないが処理されなければ困る、という今回のような場合には確実に処理が行われるようにするために [Sequence] ノードを使います。
このノードの入力ピンは上が [In]、下が [Last] となっていますが、出力されるのは [Last] ピンに入力された値です。
[In] に入力された値はこれ以降ではどこからも参照されませんが、処理だけは走ります。
これが [Set] ノードを使う条件の3つ目の回避方法です。活用していきましょう。
このようにノードを組み、[gray_threshold] を 0.5 に設定すると、三角形の形状で円が表示されるはずです。
円の数が少ないようでしたら [tile_num_x/y] の値を大きくしてあげましょう。
タイルの数がXYともに16の場合は以下のような結果となります。

最後に注意点を。
[Quadrant] ノードの [Pattern] はパラメータの関数化も出来ますし、関数内部で固定値 0 を出力すると [No Pattern] を選択されたように表示が消え、1 を出力すると [Input Image] が選択された状態となり円が表示されます。
パターンの表示/非表示はこちらでやるべきではないのか?
そう考えて [Pattern Size] を0にする方法でなく、[No Pattern] を利用した形で表示/非表示を切り替えられるようにしてみました。
しかし残念なことにこれはうまくいきませんでした。
なぜそうなっているのかはわからないのですが、どうやら [Pattern] の関数に対してはイメージのサンプリングが出来ないようです。
何か見落としてうまくいっていないだけなのかもしれませんが、私が試した際にはうまくいきませんでした。
注意しましょう。
- 2016/07/18(月) 01:04:06|
- Substance Designer
-
| トラックバック:0
-
| コメント:1
今回はWeighted Blended OITと呼ばれる技術をUE4のエンジンコードを改造して実装する話です。
エンジンコード改造となりますので文字多めです。
・OIT(Order Independent Transparency)とは?OITとはOrder Independent Transparencyの略です。
順序に依存しない半透明、というような意味ですが、ほぼそのままの意味です。
知っての通り、半透明を描画する際には描画順序が重要になります。
大抵のシステムではメッシュ単位などでZソートを行い、画面の奥のメッシュから順番に描画していきます。
この手法はほとんどどんなシーンでも有効に働きますが、一部のシーンではうまくいきません。
わかりやすいのは半透明メッシュが入れ子構造になっている場合です。
半透明のグラスの中にやはり半透明のウィスキーが入っている、という場合、正しい描画順序はグラスの奥→ウィスキー→グラスの手前となります。
しかしメッシュ単位での描画となるとウィスキー→グラス、もしくはウィスキー入りグラス1回で描画、という事にもなります。
後者はどうしようもないとして、前者はグラスの奥側を正しく描画できないので、グラスの奥は描画しないなどの措置がとられるのではないかと思います。
ゲーム中のちょっとした小物くらいならそれでもいいかもしれません。
しかし、昨今のゲームは大変映像が綺麗になっていて、カットシーンなどでも高解像度です。
また、ゲームエンジンを利用した建築ビジュアライゼーションなども需要が出てきています。
このような状況で半透明の順番を正しく考えなければならない、そういうデザインを心がけなければならないというが難しくなってきているのは否定できません。
OITを実装しているゲームはほとんどないのが現状ですが、非常に高い優先度とはいえないまでもあったら欲しい機能と考えられているのではないでしょうか?
・Weighted Blended OITとは?OITの実装としてはLinkListを利用したものが最も有名なのではないかと思います。
手前味噌ですが、私も以前実装したサンプルをアップしています。
DirectXの話 第110回この手法は新生トゥームレイダーでララ・クロフトの髪の毛の表現で使用されていましたが、やはり速度面で苦労していたようです。
正確に表現しようとするとこの手法は正確な半透明の順番を守ることが出来ます。また、トゥームレイダーでは順番を完全に正確に守らずに高速化を図っていたと記憶しています。
ある程度のスケーラビリティを持っていると言っても重いものは重い。
実装も比較的面倒ですし、このサンプルを作成した段階ではRadeonのドライバのバグで正常に描画されないこともありましたしね…
今回紹介するWeighted Blended OITは比較的軽く、実装も容易です。
Weighted Blended OITのペーパーしかしかなり大胆に近似している手法であるため、正確な半透明表現はできませんし、使用の際には注意が必要です。
概要を簡単に説明してしまうと、半透明が重なった際に、その色が最終的な映像にどの程度影響を及ぼすかを重み付けして最後に正規化するという手法です。
と言ってもわかりにくいと思いますので、実例で解説します。
不透明メッシュ描画完了時、フレームバッファには
Csというカラーが描かれていたとします。
ここに
(C1, A1)という半透明カラーが描画されたとしましょう。なお、Cはカラー、Aはアルファです。
それよりも手前に今度は
(C2, A2)という半透明カラーが描画されます。
最終結果
Cfは以下のような計算になります。
Cf = C2 * A2 + (C1 * A1 + Cs * (1-A1)) * (1 - A2) = C2 * A2 + C1 * A1 * (1 - A2) + Cs * (1 - A1) * (1 - A2)アルファ値は0.0~1.0で、アルファ値同士の乗算(
A1 * (1-A2) や
(1 - A1) * (1 - A2))は1.0を超えることはありません。
アルファ値に着目すると、最終結果により強い影響を与えるのはアルファの乗算結果が大きいカラーということになります。
つまり、
A2が大きければ
C2の影響が大きくなり、
A1が大きければ
C1の影響が大きくなります。
両方小さければ
Csの影響が大きくなります。
今度は
A1 = 0.5、
A2 = 0.5とした場合の結果を計算してみましょう。
Cf = 0.5 * C2 + 0.25 * C1 + 0.25 * Csアルファ値が固定の場合、最後に描画された
C2の影響が大きくなります。
より一般的に考えると、深度が手前の半透明カラーの方が影響を与えやすいということです。
つまり、アルファ値が大きければ大きくなる、深度が近ければ大きくなる、という感じの関数
w(a, z) を作成し、これによって各半透明カラーの影響度を求める、というのがこの技術です。
弱点としてはあくまでも各カラー単体でのブレンドウェイトしか求められないため、前後関係などを正確に表現できるというわけではない点です。
そのため、画像処理ソフトで求められたブレンドの結果はこの技術で再現することはほぼ不可能です。
また、ウェイト関数によってはカメラの移動に対して結果が安定しません。
安定させようとすると今度は小さな距離の差がほとんど無視される結果になります。
入れ子構造の半透明ではまともな結果が得られないでしょう。
と、このように制約も大きな技術ではありますが、限定的な使用方法なら用途もあるんじゃないでしょうか?
…あるかなぁ?
それはともかく、続きからでソースコードの改変を行っていきます。
[[UE4] Weighted Blended OITの実装]の続きを読む
- 2016/06/11(土) 12:42:57|
- UE4
-
| トラックバック:0
-
| コメント:0
前のページ 次のページ