もんしょの巣穴blog

引っ越しました

はてなブログに引っ越しました。
今後はこのブログの更新は行いませんのでご注意ください。

http://monsho.hatenablog.com
スポンサーサイト
  1. 2017/11/12(日) 23:09:20|
  2. 未分類
  3. | トラックバック:0
  4. | コメント:0

[SD] Substance Designer 2017.2 の新ノード Flood Fill

最近、ブログを書くのが面倒でほとんどTwitterで済ませてしまっていました。
なんとかブログを書こうという気力がほんのちょっとだけ湧いたので頑張って書いています。
いやまあ、記事を書くこと自体より、画像のコピペができないとかそういうのが面倒ってのが大きいです。
そのうち引っ越すかもしれませぬ…

今回はSubstance Designer 2017.2 が登場したので、そこで追加されているFlood Fillノードについてまとめました。

・Flood Fillとは?
Flood Fillアルゴリズムというものがあります。
これは画像処理アルゴリズムの1つで、画像内の特定アイランドに色を充填するためのアルゴリズムです。
簡単に言ってしまえばPhotoshopのペンキ缶ですね。
SDの[Flood Fill]ノードはこのアルゴリズムを用いてアイランドごとに情報を持たせているものと思われます。

・何ができる?
白黒で何らかのパターンを作るのはSDではよく行われますが、それをそのままハイトマップやノーマルマップにしてしまうとあまりにも整列しすぎていて不自然です。
だからと言って適当なノイズを与えるだけだとそれはそれで不自然だったりします。
出来ればアイランドごとにIDを持たせるような印象で情報を割り振り、それを元にして他のマップを生成したいという場面があります。

[Tile Generator]ノードなどはその手の情報をランダムに生成してくれる機能がありますが、[Cells 3]のようなノイズノードではアイランドごとに情報をランダムに変更する機能はありません。

[Flood Fill]ノードとそれに続くノードを用いることでこのような情報をあとから生成することができるようになった、というのが今回のアップデートの大きなところです。

・使い方
まず、[Flood Fill]ノードに白黒のパターン画像を接続します。
すると、なんだか不可思議な画像が出来上がります。
この画像はこのままでは基本的に使えませんが、ここには各アイランドに関する情報が保存されているようです。
どのような情報が保存されているかは不明ですが、アイランドのバウンディングボックスのサイズなんかは入っているようです。

この[Flood Fill]ノードの出力を[Flood Fill to ~]ノードに繋ぐと、その繋いだノードに合わせた情報を取得することができます。

2017-10-21_18h12_39.png

[Flood Fill]ノードに続くノードは5種類あります。

[Flood Fill to BBox Size]は各アイランドのバウンディングボックスサイズをアイランドに割り当てます。アイランドの大きさを求めることができるわけです。

[Flood Fill to Gradient]は各アイランドにグラデーションをかけることができます。
グラデーションの方向とランダム性を指定することができるため、かなり使い勝手がいいのではないでしょうか?

[Flood Fill to Position]は各アイランドの中心座標を色として割り当てます。

[Flood Fill to Random Color/Grayscale]は各アイランドにランダムにカラー/グレースケールを割り当てます。

これらのノードは[Flood Fill]ノードから繋げなければほとんど意味がないノードです。
かならず[Flood Fill]と一緒に使いましょう。

・作例
以前のSubstance勉強会の時に作った石畳は石の形状を作成するのにちょっと面倒な方法を採用していました。

2017-10-21_22h19_18.png

[Histgram Shift]を用いて無理やりノーマルマップを作り、そこからハイトマップに戻す方法ですね。
勉強会ではこのようなハイトマップを2種類作り、ブレンドしていました。
この方法でも悪くはないのですが、胡散臭い形状ができてしまうこともあって調整が面倒です。

[Flood Fill]を使ってよりスマートで安定的な形状を作る方法がAllegorithmicの公式動画にあります。

2017-10-21_22h38_11.png

[Flood Fill to Gradient]ノードを用いて各アイランドに傾斜をつけます。
この傾斜をそれぞれ別方向で3つ作り、すべてをMinブレンドします。
するとこのようなノーマルマップが生成できます。

2017-10-21_22h41_12.png

安定的な星型の形状ができました。
この状態だとあまりにも綺麗すぎるので、

・[Flood Fill to Gradient]の[Multiply by Bounding Box Size]パラメータを調整して星の中心をアイランドの重心付近から移動させる
・[Flood Fill to Random Grayscale]を使ってマスクを作り、星の足の数をアイランドごとに配置する

などの方法でよりランダム性を出すことができるでしょう。

・不具合?
不具合なのかアルゴリズム的な仕様なのかは不明ですが、[Flood Fill]を使用した場合に一部のアイランドのエッジ部分にノイズが乗ることがあります。

2017-10-21_22h49_29.png

[Flood Fill]の入力にゴミが残っているというわけでもなく、角度的な問題という感じもしませんが、何らかの理由でこのようなノイズが出てしまいます。
形状を変えたりすれば出なくなったりするのですが、出てしまうのがそもそも困るわけで。

対処の方法としては、[Flood Fill]へ入力するノードに[Blur HQ Grayscale]で少しだけブラーをかけてやると出なくなったりします。
ブラーの強度は0.2くらいなら形状に影響も与えず、ノイズ問題も解消できるようです。
ただし、どんな形状に対してもノイズ問題を解決できるかは不明なので注意してください。

以上、[Flood Fill]ノードの紹介でした。
  1. 2017/10/21(土) 22:55:21|
  2. Substance Designer
  3. | トラックバック:0
  4. | コメント:0

[UE4] ベースパスでレイマーチング

家弓メソッドを利用したレイマーチングはGitHubにアップしてあるMaterialCollection内にサンプルが存在していたりします。

しかし、こちらのサンプルは基本的にライティングなどをマテリアル内で行うことを前提として作成しました。
そのため出力はEmissiveのみ、Unlitで行うことを前提としています。
というか、ポストプロセスで利用するのが基本、という感じで作成しています。

少し前に、ある人物からこんなことを聞かれました。

「レイマーチングで描画したものに影を落とすとしたらどうやります?」

それは面白そうだ、やってみよう。ということでやってみました。

家弓メソッドを用いた距離関数の切り替え手法とレイマーチングの行い方は説明しません。
MaterialCollectionの中を見ていただければわかるのではないかと思います。

今回はその結果をEmissiveに出力するのではなく、ベースパスでちゃんと法線を持ったオブジェクトとして描画してみようと思います。

まずマテリアルの Shading Model は Default Lit にします。Blend Mode は Masked にしましょう。まずはここから。
次に距離関数を設定したマテリアルファンクションとレイマーチング命令を内包したマテリアルファンクションを加算します。

ue419.jpg

加算した結果はあとで別の値に対して加算して、ちゃんとコード生成が行われるようにしましょう。

私が作成したレイマーチング用の関数はCustomノードで使用することを前提としており、1つの関数からワールド座標、ワールドノーマル、レイが衝突したかどうかのフラグを取得することが出来ます。
しかしCustomノードは1つのノードで出力可能な値は1つだけです。1つのCustomノードで3つの戻り値を返すことが出来ないのです。
なので同じ命令を実行し、出力する値をそれぞれ違うものを出力するようにします。

ue420.jpg

衝突したかどうかは [GetDistanceFieldMask] というCustomノードをチェックします。
このノードの出力は、衝突していたら 1.0 を、そうでなければ 0.0 を出力するように出来ています。
なのでそのままOpacity Maskに入力すればOKです。

Normal も基本的にそのままマテリアル入力に接続しますが、DistanceField関数を定義しているノードを有効化するためにそれらのノードとの加算を取ります。

ue421.jpg

また、接続するノーマルはワールド空間ノーマルなので、マテリアルパラメータの [Tangent Space Normal] はOFFにしておきましょう。

最後に座標ですが、実際に描画されているオブジェクトとDistance Fieldから深度方向のオフセットを求めます。
レイマーチングの計算で得られた座標と元のワールド座標をカメラ空間に変換し、その差をDepth Offsetに接続します。

ue422.jpg

あとはカラーを適当な値で出力すればOKです。

このマテリアルをアサインするメッシュは適当な板でOKです。
この板は常にカメラの目の前に置くようにすると、シーン全体でレイマーチングを行うようになります。

ue423.jpg

コリジョンはなしにしておいたほうが良いでしょう。

今回の手法はベースパスで処理されるため、ライティングもシャドウレシーブも行われます。
ただ、シャドウキャストはうまくいきません。シャドウキャストはOFFにしておくのが良いでしょう。

もちろん、実際に描画されている物体にコリジョンはありませんので、衝突させたくてもうまくいきません。
とまあ…正直なところ、使い物にならない技術かな、と思います。

  1. 2017/01/24(火) 00:55:31|
  2. UE4
  3. | トラックバック:0
  4. | コメント:0

[UE4] エンジンを拡張してポストプロセスを実装する おまけ

この記事はポストプロセスをエンジン改造で実装する際にハマった罠について書いています。
エンジン改造してまでポストプロセスの実装なんてしないよ、という方には不要かもしれません。

コンソールゲーム機ではフレームバッファの解像度をユーザが変更できる機能は通常存在しません。
処理落ち回避のために動的にフレームバッファの解像度を変更するゲームがあったりはしましたが、あまり存在していません。

しかし、PCゲームでは解像度を変更する機能は普通に存在しています。
ましてやUE4やUnityのようなゲームエンジンはエディタ上でゲームを動かせるため、エディタ上のゲームViewのサイズが変更されることも多くあります。

解像度が変更された場合、通常であればフレームバッファや、ポストプロセスなどで処理を行うためのオフスクリーンバッファは解像度に合わせて作り直されるのが一般的です。
UE4でも基本はそうなのですが、ある状態変化の場合はフレームバッファが作り直されません。
その状態というのは、解像度が小さくなった場合です。

まずはこの画像をご覧ください。

ue416.jpg

これはUE4のエディタを小さめにして使っていた場合の途中のレンダーターゲットです。
では次。

ue417.jpg

こちらはエディタを最大化した際のレンダーターゲットです。
当然ですが解像度は縦も横も上がっているので、フレームバッファやオフスクリーンバッファは作り直されています。

では最後。

ue418.jpg

エディタの最大化を解除して、最初と同じウィンドウサイズに変更した場合のレンダーターゲットです。
見ての通り、最大化したサイズのまま、描画される範囲だけが小さくなっています。

この動作はシッピングされたゲームでも同様の動作をするのかは不明ですが、少なくともエディタ上ではこのように動作します。

ポストプロセスを実装する際、フレームバッファとサイズの異なるバッファを使用することはよくあります。主に縮小バッファとして。
しかし、単純にバッファサイズのポリゴンを描画するような方法を取ってしまうと正常に描画されなかったり無駄なピクセルシェーダが動いてしまったりします。
なので、正しいサイズで、正しい範囲に描画を行わなければなりません。

今回ピクセルシェーダで実装したディフュージョンフィルタでは他のポストプロセスに習って DrawPostProcessPass() という命令を使用しています。
この命令は出力先のレンダーターゲットに対して適切なUV座標を持った板を描画してくれる命令です。
ここに適切な値を渡してやればバッファサイズがどんなに変わってもちゃんと描画されますが、適切でないとうまくいきません。
なので必要なパラメータを少し紹介します。

float X, Y, SizeX, SizeY
これはレンダーターゲットに対するViewportの左上座標とサイズです。単位はピクセル。

float U, V, SizeU, SizeV
これはポストプロセスを適用する元のバッファのUV値の左上座標とサイズです。こちらも単位はピクセル。

FIntPoint TargetSize
これはレンダーターゲットのバッファの縦横のサイズです。単位はピクセルで、与えるのは整数値です。

FIntPoint TextureSize
これは元のバッファの縦横のサイズです。単位はピクセル、整数値で与えます。

この関数は簡単なポストプロセスには比較的簡単に適用できるのですが、複数のサイズが異るバッファを使った場合はうまくいかない可能性も出てきます。
複数のテクスチャにおいて使用する正規化されたUV値が同一でOKならさほど問題はないです。ちなみに、ディフュージョンフィルタでは同一UV値でOKだったので正常動作しています。
比率がぜんぜん違うテクスチャを複数枚使う場合はこの命令を参考に自分たちの用途に見合った命令を作成しましょう。

というわけで、ちょっとしたおまけでした。
  1. 2016/12/17(土) 00:16:32|
  2. UE4
  3. | トラックバック:0
  4. | コメント:0

[UE4] エンジンを拡張してポストプロセスを実装する CS編

この記事は裏UE4 Advecnt Calender 2016の15日目の記事です。

UE4のポストプロセスはマテリアルを利用して比較的簡単に拡張が可能です。
しかしながら、いくつかの問題点も存在しています。

問題の1つにCompute Shaderが使用できない、というものがあります。

Compute Shaderはラスタライザを使用せずにGPUの高速な演算機能を使用する手段で、DX11世代以降で追加されたシェーダです。
特にDX12やVulkanではグラフィクス処理と並列に処理を行うことが出来るコンピュートパイプと呼ばれるパイプラインが存在し、これを利用した非同期コンピュートはグラフィクス用途でもそれ以外の用途でも使用できます。
グラフィクス処理を行うパイプラインでも使用することは出来ますが、ここで使用する場合はコンテキストスイッチなどが発生するようで、場合によってはラスタライザを経由するより処理速度が落ちます。

しかし、Compute Shaderには共有メモリが存在します。

Pixel Shaderは各ピクセルごとに独立して処理が行われます。
そのため、テクスチャのサンプリングはそれぞれのピクセルごとに行われ、別々のピクセルで同一テクセルをサンプリングしたとしてもその情報を共有することは出来ません。

Compute Shaderは一連の処理を複数回行う際、複数の処理の束をグループとしてまとめることが出来ます。
グループ内の処理は基本的には独立しているのですが、グループ単位で少ないながらも共有メモリが使用でき、また、グループ内の処理の同期をとることが出来ます。
これを利用することで、別々のピクセルでの処理に同一テクセルを大量にサンプリングされる場面で共有メモリを介して高速化することが可能です。

今回は以前ポストプロセスマテリアルで実装したSymmetric Nearest Neighbourフィルタをエンジンを改造してCompute Shaderで実装しました。
ただ、大変残念なお知らせですが、普通にPixel Shader使うより遅いです。
コンテキストスイッチが発生してるなどで処理速度が遅くなっている可能性が高いですが、まだ何が原因かは調べていません。
余裕があったら調べようとは思いますが、わかっているのはPixel Shaderでの実装より倍遅かったです。
メモリ帯域が狭いGPUの場合は共有メモリによる高速化が効いてくるはずですが、GTX1070ではさほど効果がないようです。

ue415.jpg

PostProcessSNNが今回実装したCS版SNNで、その下の "M_SNN_Filter" がポストプロセスマテリアルで実装したPixel Shader版のSNNです。
SNNCopyという描画パスについては後にご説明します。

では、続きからで実際の実装方法を見ていきましょう。

[[UE4] エンジンを拡張してポストプロセスを実装する CS編]の続きを読む
  1. 2016/12/15(木) 00:04:42|
  2. UE4
  3. | トラックバック:0
  4. | コメント:0
次のページ

プロフィール

monsho

Author:monsho
ゲームプログラマ?

最近の記事

最近のコメント

最近のトラックバック

月別アーカイブ

カテゴリー

ブロとも申請フォーム

この人とブロともになる

ブログ内検索

RSSフィード

リンク

このブログをリンクに追加する