【UE5】モザイク機能を作ってみた – Unreal Engine 5.6

Unreal Engine

水ノ茉(こおり)の宣伝

叡智でえっちな同人作品を予定中…

Ci-en R18

同人作品の宣伝

  • 準 備 中 . . .

プラグインの宣伝

始まり

ぁぁーだめだめッ、えっち過ぎます!

えっちな作品で利用するえっちな機能モザイクを叡智を率いて作っていきます。

我ながら発言が意味不明です。

えっち叡智なの。

リポジトリ

準備中。

気が向いたら公開するわ。

ポストプロセスマテリアルで作ってみる

ブロックモザイクを作成していきます。

計算方法はとてもシンプルです。

  1. カラーの合計値を算出
  2. 合計値を面積で除算して平均値を算出
  3. 求めた平均値で塗り潰す

あまりにも簡単過ぎるので実際のコードを読んだ方が理解が早いと思います。ポストプロセスマテリアルのカスタムノードで組むとこんな感じです。

BRANCH
if (BlockSize == 0)
{
    return float3(0.0, 0.0, 0.0);
}

float2 PixelPos = UV * BufferSizeAndInvSize.xy;
float2 BlockOrigin = floor(PixelPos / BlockSize) * BlockSize;

float3 ColorSum = float3(0.0, 0.0, 0.0);

LOOP
for (int x = 0; x < (int)BlockSize; ++x)
{
    LOOP
    for (int y = 0; y < (int)BlockSize; ++y)
    {
        float2 NewUV = (BlockOrigin + float2(x, y) + float2(0.5, 0.5)) * BufferSizeAndInvSize.zw;
        float2 NewClampedUV = ClampSceneTextureUV(ViewportUVToSceneTextureUV(NewUV, PPI_PostProcessInput0), PPI_PostProcessInput0);
        float3 NewSceneColor = SceneTextureLookup(NewClampedUV, PPI_PostProcessInput0, 0).rgb;
        ColorSum += NewSceneColor;
    }
}

return ColorSum / Pow2(BlockSize);

簡単にブロックモザイクが実装出来ました。

さてと、えっちにしか興味のない方々はここで終わりでもいいでしょう。

ここからは叡智な領域です。

ブロックモザイクを愚直に実装すると処理負荷がえげつないことになります。

主にこの部分です。計算量にO(N2)を要求しています。

LOOP
for (int x = 0; x < (int)BlockSize; ++x)
{
    LOOP
    for (int y = 0; y < (int)BlockSize; ++y)
    {

プロファイラ結果を見るとこれが如何に恐ろしいかが分かります。

上から順にブロックサイズが8、16、32ピクセルの処理負荷です。単位はnsナノ秒です。

負荷の伸び方が狂っています。恐ろしいです。

ブロックサイズは64ピクセルまでサポートしたい気持ちがあります。

モザイク処理に1msミリ秒以上割くなんてアホアホです。叡智とは程遠いです。

そんな訳で、最適化をしつつ、プロダクトに寄り添った機能をいくつか実装していきます。

コンピュートシェーダーで最適化してみる

先ほどのモザイク処理は所謂、平均化フィルタです。

CSCompute Shaderと相性が抜群に良いことは想像に容易いでしょう。

PSPixel ShaderとCSの違いのひとつに共有メモリがあります。

共有メモリ、Local Data ShareLDSThread Group Shared MemoryTGSM

色々な呼び方があります。余程気難しい方でなければ、どの呼び方でも伝わると思います。好きな音を選ぶとよいでしょう。筆者はLDS派です。LDSの説明はネットの海に転がっていると思うので興味があれば拾ってきてください。

最適化の方針としてはこんな感じです。

  1. カラーのフェッチ結果をLDSに格納
  2. 平均値をLDSから算出
  3. 爆速!!!

コードで書くとイメージしやすいです。

// LDSの宣言
groupshared float3 SharedSceneColor[8 * 8];

// LDSに格納
SharedSceneColor[xx] = SceneTextureLookup(...).rgb;

// SharedSceneColorが埋まるのを同期待ち
GroupMemoryBarrierWithGroupSync();

// 平均値算出
for (xxx)
{
	// LDSから値を取り出すから SceneTextureLookup のコストがO(N2)から解放される
	xxx += SharedSceneColor[xx];
}

最適化後のプロファイラ結果がこの通りです。負荷が大幅に改善されていて且つ、O(N2)から解放されたことでブロックサイズを変えても負荷がほとんど変わらなくなりました。コンピュートシェーダーちゃんは最強なのです。

スフィアマスク×モザイク

局部モザイクに対応するために任意座標を中心にスフィアマスクを実装です。

隠したい場所に合わせてソケットを追加、配置するだけで簡単に運用できます。

マスク挙動の改善

マスクをブロックごとに適用して、くり抜き感を解消です。

ブロックサイズを距離で切り替え

ブロックサイズを距離に応じて変えられるようにしました。所謂LODというやつです。

ブロックサイズの切り替えをスムーズに

LODの切り替えをフェードしてパカパカを解消です。

おわり!!!

一見すると単純なモザイク機能ですが、その中身には意外と叡智が詰め込まれていたりするのです。

気分が冷めなければ叡智を詰め込んだえっちな作品をリリースする予定なのです。興味があれば付き合ってね。

かふえすの記事一覧 - Ci-en(シエン)
かふえすの記事一覧です。「すっぽんぽんより着衣がえっち」「動きを付けるとそれっぽい雰囲気」などの記事が投稿されています。 - Ci-en(シエン)

雑談

内容が随分と薄いです。

何故でしょうか。

それはクロスに浮気していたからです。

モザイクへの興味は完全に薄れました。

筆者は超が付くほどの気分屋さんです。

一度冷めたら終わりなのです。

えちえち Cloth Simulation Season 3

構成、せん断、曲げ制約を復活です。

異なるパーツ間の曲げ制約です。これで独自の制約構築方法でも幅広く対応できるようになりました。

CofeeLiveで実装したS1はガウス法を採用しましたが、S2~S3ではジャコビ法です。色分けをするとDispatchを量産する羽目になるのでそれを避けるためですね。

クロスシミュの計算結果をレンダーメッシュに埋め込みです。緑色がクロスメッシュ、赤色がレンダーメッシュですね。

元の形状を維持する機能を新設です。身体のラインに沿ってカプセルコリジョンを設置するのが面倒だったので、そのあたりの簡略化を兼ねています。

CoffeeLiveと異なりBoobはえっちするだけです。アニメ変位は高が知れているので、それよりも身体に埋まることの対策を優先です。設計上抑制できるであろうという机上なので、効果はまだ分からんですけどね。

カプセルコリジョンの実装です。

プラグイン設計なGPGPUクロスシミュの爆誕です。

プラグイン化という意義は達成しましたが、やはり代償として処理負荷の劣化が目立ちます。非同期コンピュートはバッチの投下タイミングが遅すぎて、結局は同期待ちコストが勝っちゃいました。少しずつ改善してきましょうか。

少しずつ改善と言いつつ、最適化にドハマりした結果、一気に進めちゃいました。10倍ほど高速化です。GPGPUはライブラリ開発している感を最高に感じられるのでマジで楽しすぎる。

負荷的に現実ラインであるサブステップ5回はこんな感じです。

えっちなプロダクトを前提としているため、すり抜け防止は結構強めです。貫通する見た目って萎えるんですよね。知らんけど。

むふふ。我ながら最高にいい子が出来ました。

これでReinシリーズはソフボとクロスのラインナップです。

戦える手札が日に日に増えていくこの感じが最高に気持ちいいですね。

ソフボの時も言いましたが、マジでコスパの悪いオナニーです。

この快楽は開発者にしか分からんやろ。ええやろー。むふふー。