アルファテストの改良

こんばんみん。
Pocolです。

ネットで記事を漁っていたら,アルファテストの品質向上の手法についての記事を見つけました。
https://asawicki.info/articles/alpha_test.php5

以前ゲーム開発をしていた際に,キャラクタの髪の毛や動物の毛周りで困ることがあったので,ハッシュ化アルファテストなどを試してみたのですが,結構ちらつきがやっぱりきになっちゃうなーと思っていましたし,意外と計算量多いんですよね。もっと手軽でそれっぽい方法無いかなーって常々思っていたのですが,記事で紹介している手法はかなりシンプルなので,個人的には「これでよくね?」って感じています。
アルファ値を次のように変えるのと,事前準備としてPhotoShopなどでSolidifyフィルタを使用してテクスチャエッジ部分の色を引き延ばしてテクスチャを作成しておけばよいみたいです。

[hlsl]
float alphaNew = max(alpha, (1.0/3/0) * alpha + (2.0/3.0) * threshold);
if (alphaNew < threshold)
discard;
[/hlsl]

Wave組み込み命令トリック

こんばんわ。
Pocolです。

Angry Tomato!さんという方が,“Compute shader wave intinsics tricks”
という記事を書いているので紹介です。
この記事では,以下のテクニックを紹介しています。

  • Branch optimization
  • Calculate on one lane, read on all
  • Serialization of Writing Data
  • Scalarization
  • Multiple wave parallelization
  • Indirect dispatch thread group count calculation
  • Dividing the work between lanes

非常に面白い内容だと思うので,見ていない方は是非見るとよいでしょう。

WaveActiveLerp()について

こんちゃわ。Pocolです。
Wave組み込み命令の記事を漁っていたら,GithubにWaveActiveLerp()の実装を書いている人がいたので紹介しようと思います。
下記に説明の記事があります。
https://github.com/AlexSabourinDev/cranberry_blog/blob/master/WaveActiveLerp.md

実装は,https://github.com/AlexSabourinDev/cranberry_blog/blob/master/WaveActiveLerp_Shaders/WaveActiveLerp.hlslにあって,次のような感じみたいです。

[hlsl]
uint WaveGetLastLaneIndex()
{
uint4 ballot = WaveActiveBallot(true);
uint4 bits = firstbithigh(ballot); // Returns -1 (0xFFFFFFFF) if no bits set.

// For reasons unclear to me, firstbithigh causes us to consider `bits` as a vector when compiling for RDNA
// This then causes us to generate a waterfall loop later on in WaveReadLaneAt 🙁
// Force scalarization here. See: https://godbolt.org/z/barT3rM3W
bits = WaveReadLaneFirst(bits);
bits = select(bits == 0xFFFFFFFF, 0, bits + uint4(0, 32, 64, 96));

return max(max(max(bits.x, bits.y), bits.z), bits.w);
}

float WaveReadLaneLast(float t)
{
uint lastLane = WaveGetLastLaneIndex();
return WaveReadLaneAt(t, lastLane);
}

// Interpolates as lerp(lerp(Lane2, Lane1, t1), Lane0, t0), etc
//
// NOTE: Values need to be sorted in order of last interpolant to first interpolant.
//
// As an example, say we have the loop:
// for(int i = 0; i < 4; i++)
// result = lerp(result, values[i], interpolations[i]);
//
// Lane0 should hold the last value, i.e. values[3]. NOT values[0].
//
// WaveActiveLerp instead implements the loop as a reverse loop:
// for(int i = 3; i >= 0; i–)
// result = lerp(result, values[i], interpolations[i]);
//
// return.x == result of the wave's interpolation
// return.y == product of all the wave's (1-t) for continued interpolation.
float2 WaveActiveLerp(float value, float t)
{
// lerp(v1, v0, t0) = v1 * (1 – t0) + v0 * t0
// lerp(lerp(v2, v1, t1), v0, t0)
// = (v2 * (1 – t1) + v1 * t1) * (1 – t0) + v0 * t0
// = v2 * (1 – t1) * (1 – t0) + v1 * t1 * (1 – t0) + v0 * t0

// We can then split the elements of our sum for each thread.
// Lane0 = v0 * t0
// Lane1 = v1 * t1 * (1 – t0)
// Lane2 = v2 * (1 – t1) * (1 – t0)

// As you can see, each thread's (1 – tn) term is simply the product of the previous thread's terms.
// We can achieve this result by using WavePrefixProduct

float prefixProduct = WavePrefixProduct(1.0f – t);
float laneValue = value * t * prefixProduct;
float interpolation = WaveActiveSum(laneValue);

// If you don't need this for a continued interpolation, you can simply remove this part.
float postfixProduct = prefixProduct * (1.0f – t);
float oneMinusT = WaveReadLaneLast(postfixProduct);

return float2(interpolation, oneMinusT);
}
[/hlsl]

いまのところで,使いどころがパッと浮かばないのですが,知っていればどこかで使えそうな気がしています。
…というわけで,WaveActiveLerp()の実装紹介でした。

WaveCompactValue()について

最近、最適化で忙しいPocolです。
皆さん、お元気でしょうか?

今日は,WaveCompactValue()を勉強しようかなと思いましたので,そのメモを残しておこうと思います。
この関数は,[Drobot 2017]で紹介された手法です。

スライドに掲載されている実装は下記のよう感じです。
[hlsl]
uint WaveCompactValue( uint checkValue )
{
ulong mask; // lane unique compaction mask
for ( ; ; ) // Loop until all active lanes removed
{
uint firstValue = WaveReadFirstLane( checkValue );
mask = WaveBallot( firstValue == checkValue ); // mask is only updated for remaining active lanes
if ( firstValue == checkValue ) break; // exclude all lanes with firstValue from next iteration
}
// At this point, each lane of mask should contain a bit mask of all other lanes with the same value.
uint index = WavePrefixSum( mask ); // Note this is performed independently on a different mask for each lane.
return index;
}
[/hlsl]

これをHLSLに書き直すと次のような感じになるかとおもいます。
[hlsl]
uint WaveCompactValue(uint checkValue)
{
  // レーンのユニークなコンパクションマスク.
uint4 mask;

// すべてのアクティブレーンが取り除かれるまでループ.
for (;;)
{
// アクティブレーンの最初の値を読み取る.
uint firstValue = WaveReadLaneFirst(checkValue);

// mask は残っているアクティブレーンに対してのみ更新される.
mask = WaveActiveBallot(firstValue == checkValue);

// firstValue を持つすべてのレーンを次のイテレーションから除外する。
if (firstValue == checkValue)
break;
}
// この時点で、マスクの各レーンは、同じ値を持つ他のレーンのすべてのビットマスクを含んでいなければならない。
uint index = WavePrefixSum(mask); // これはレーンごとに異なるマスクで独立して行われる。
return index;
}
[/hlsl]

さて,このWaveCompactValue()ですが,どういった使い道があるかというと,分類分けに使用することができます。
元々の[Drobot 2017]では色々なスレッドからAtomic操作をすると重くなるため,Atomic操作を減らす目的のために使われていました。
詳細な説明は,[Drobot 2017]のスライド51にアニメーション付きで載っていますので,そちらを参照してください。
軽く図の説明だけ載せておきます。




ちなみにグループ分けのよう番号を別途作りたい場合は,
[hlsl]
uint2 WaveCompactValue(uint checkValue)
{
  // レーンのユニークなコンパクションマスク.
uint4 mask;

// グループ分け番号.
uint groupIndex = 0;

// すべてのアクティブレーンが取り除かれるまでループ.
for (uint i=0; ; ++i)
{
// アクティブレーンの最初の値を読み取る.
uint firstValue = WaveReadLaneFirst(checkValue);

// mask は残っているアクティブレーンに対してのみ更新される.
mask = WaveActiveBallot(firstValue == checkValue);

// グループ分け番号を更新.
groupIndex = i;

// firstValue を持つすべてのレーンを次のイテレーションから除外する。
if (firstValue == checkValue)
break;
}
// この時点で、マスクの各レーンは、同じ値を持つ他のレーンのすべてのビットマスクを含んでいなければならない。
uint index = WavePrefixSum(mask); // これはレーンごとに異なるマスクで独立して行われる。
return uint2(index, groupIndex);
}
[/hlsl]
のように実装すると良いみたいです。
WaveCompactValue()はタイルの分類分けやマテリアルの分類分けなんかの場面で有効活用できそうな気がしています。
…というわけで,WaveCompactValue()を使って分類分けすれば,無駄なAtomic操作を減らせるので,高速化できるよ!という話でした。

参考文献

・[Drobot 2017] Michal Drobot, “Improved Culling for Tiled and Clustered Rendering Call of Duty Infinite Warfare”, SIGGRAPH 2017 Advances in Real-time Rendering and Games course, https://advances.realtimerendering.com/s2017/index.html

CEDEC 2024 -Day3-

こんにちわ、Pocolです。

CEDEC最終日は以下の講演を聴講しました。

  • 知る・創る・繋ぐ『ゼルダの伝説 ティアーズオブザキングダム』で再構築した開発環境とサウンド制作事例
  • アーティストのためのカメラの仕組み講座
  • 『ゼルダの伝説 ティアーズオブザキングダム』におけるフィールド制作とQA~トーレルーフの裏側で~
  • VFXを物理的な数値を基準に作ろう「FINAL FANTASY XVI」開発環境
  • ペルソナ3 リロードでの自動プレイの実装と運用

知る・創る・繋ぐ『ゼルダの伝説 ティアーズオブザキングダム』で再構築した開発環境とサウンド制作事例

非常に良い講演でした。NintendoWareとかだとツールが全部別れていて,しかも設計者・実装者がツールごとに違う。でも,ゲームを作るうえで連携したい!じゃ、どうやって解決したのか?…という感じの講演内容です。データベースを用意して,データベース経由で各ツールからメッシュやボーン・アニメーションといった感じの情報にアクセスして解決しているようです。
ここで問題になるのが,どこにデータベースを置くのか?ということですが,これについてもきちんと説明されていました。ローカル用とグローバル用の二つのデータベースを用意し解決しているようです。統合型環境ではない場合,非常に参考になる部分があると思うので,是非CEDIL等の資料をみることをお勧めします。

アーティストのためのカメラの仕組み講座

実演等があり,非常に分かりやすい内容でした。スマホのレンズの話から,焦点距離・画角の話,Aapature Value,ISO感度,自動露出,ホワイトバランス,一通りの説明がありました。
資料後半にカメラと同じ順番でポストプロセスを入れた方が良いという話があったので,エンジンがこれに準じた順番になっていない場合は,処理を見直した方がよさそうです。

『ゼルダの伝説 ティアーズオブザキングダム』におけるフィールド制作とQA~トーレルーフの裏側で~

トーレルーフが実はデバッグ用途の機能な施策を元に作られているという話でした。まずは,ボクセルを用いた周辺情報の収集の仕組みがあり,レイキャストを用いる場合と比べて10倍近く高速化したそうです。つづいて,QAエンジニア目線での話に移り,デバッグ機能の充実やテスターと開発者の隔たりを無くす施策などが語られました。次にアーティスト側でコリジョン抜けチェックや洞窟システムの話があり,これらのものを作ってトーレルーフが実装されているそうです。一見するとトーレルーフ要に特殊な仕組みで実装したんじゃないかと思いますが,実はありものの機能を使って実装しました。…という内容でした。でも,そもそもそこまでちゃんとした機能って普通のゲーム会社作れるところって少ないよな,自分たちの場合だったらどうすればいいんだろうか?そういう課題点も聞いていて感じました。任天堂は物凄く真っ当なことをされているなと感じました。

VFXを物理的な数値を基準に作ろう「FINAL FANTASY XVI」開発環境

特に参考になる部分はあんまりなかったですし,物理ベースレンダリングの話は結構出ていて,過去の文献をまったく調べていないじゃないか?というような知り合いとの話もありました。
逆に言うとそういう知識が無い人はある程度聞きやすいのかもしれません。

ペルソナ3 リロードでの自動プレイの実装と運用

SEGA テックブログで関連する記事があるそうです。非常に参考になる内容でした。ルールベースで移動させるとか,出来るだけゲーム側に手を入れないようにプラグインの形で実装するとか,実装する上で非常に参考になります。また対応にどれぐらいの工数がかかったかなども触れられており,QA作業工数の大幅現に成功したそうです。デバッグチェック用の工数削減に興味がある方は是非見てみると良いと思います。

CEDEC 2024 -Day2-

おはようございます。
Pocolです。
技術的な内容はCEDILを見ればいいと思うので,面白かった点や良かった点だけを軽く述べることにします。

昨日は以下の講演を聴講しました。

  • モバイルにも使える軽量な構造を持つ仮想化ジオメトリシステムの設計と実装について
  • Unityにおける大量オブジェクトのレンダリング高速化事例~GPU駆動レンダリング&Hi-Zカリングの統合~
  • Mesh Shaderを活用したスキニングメッシュに対するサブディビジョンサーフェイス
  • 圧倒的キャラクター数×カスタマイズ数をゲームエンジンで実現~『鉄拳8』キャラクターグラフィックス事例
  • 『ゼルダの伝説 ティアーズオブザキングダム』の世界をつなぐ技術~空、地上、地底、そして制作もシームレスに~

モバイルにも使える軽量な構造を持つ仮想化ジオメトリシステムの設計と実装について

雑に言うとNaniteみたいなシステムですが,結構モバイルで対応しようとするとV-Buffer使いづらいとか,Wave組み込み命令が遅くなる場合もあるとか,モバイル特有のクセみたいなのがあって大変だなーと思いました。
デモを実際にやっていたのですが,どのスマホの機種なのかまでは明示されていなかったので、どのぐらいのスペックで動かしているかまではわかりませんでした。

Unityにおける大量オブジェクトのレンダリング高速化事例~GPU駆動レンダリング&Hi-Zカリングの統合~

2-Phaseのオクルージョンカリングでした,以前にこのBlogでも紹介したような1-Phaseの手法はとっていないみたいでした。
GPU駆動レンダリングとかまったくわからない方は一度見ておくと勉強になるかもしれません。
データは種類ごとに配列にしていました(SoA)。やっぱりそっちのほうがやっぱりいいのかなと思いました。自分が実装したときはAoSだったのでアクセス効率が悪いなどの問題があり,ちょっとシェーダ上で問題ある箇所もありました。次に実装するときはSoAにようと思います。

Mesh Shaderを活用したスキニングメッシュに対するサブディビジョンサーフェイス

非常に参考になった。
以前にCEDECでDX11でテッセレーションの発表でも言っていましたが,シルエットはそもそもいい感じ作ってあるので,テッセレーションしてもあんまり変化がわからないなーという印象を受けました。技術的には面白いと思いますが,まぁただゲームに使いますか?っていわれると個人的には「うーん…」という感じです。

圧倒的キャラクター数×カスタマイズ数をゲームエンジンで実現~『鉄拳8』キャラクターグラフィックス事例

カスタマイズ性のあるゲームを作成する際は非常に参考になると思います。
講演の中でしわの表現をポリゴンの面積に応じて法線ブレンド率を変えて実現するというのが個人的にはめっちゃ使える技術だと思いました。

『ゼルダの伝説 ティアーズオブザキングダム』の世界をつなぐ技術~空、地上、地底、そして制作もシームレスに~

洞窟システムの話をしていました。
Edge Collapseみたいな感じで,段々と頂点をマージしていきポリゴンを減らすという手法をとっていて,頂点がうねうね動いていました。ハイトマップのTerrainのみだとやっぱり洞窟みたいなのは結構作りづらいので,オープンワールドゲーム作っている人には非常に参考になる内容だったのではないでしょうか。あとはテクスチャの適切な解像度の判定方法や,カリングするために撮影点をCIで回して記録しておくとか,みんなどうやって解決しているんだろうっていう部分の一部が垣間見えて良かったです。
こちらの講演は会場でほぼ満員でした。非常にためになるので,テレイン周りとか作っている方は見たほうが良いかと思います。

CEDEC 2024 -Day1-

こんばんわ。Pocolです。
CEDEC1日目が終了しました。
今日は以下の講演を聴講しました。

  • 鉄拳シリーズを通してみた格闘ゲームの変遷とその未来
  • “Game Survey”で開発・デバッグ効率が向上した取り組み AtoR from FINAL FANTASY XVII
  • PlayStation5上で人間のプレイヤーと同条件でのゲームプレイ自動化を実現するAI技術
  • プロシージャルゲームコンテント制作ブートキャンプ
  • リアルタイム光学エフェクトの深淵~究極表現への道と位置~
  • 『FINAL FANTASY VII REBIRTH』における会話イベントの量産とアニメーションワークフロー
    • 鉄拳シリーズを通してみた格闘ゲームの変遷とその未来

      とある諸事情で基調講演が事前収録に。
      鉄拳シリーズ30周年で,PlayStationで一番最初にミリオンを達成したのが鉄拳2とか,色々と多くのギネス記録を保持していて,1232件ぐらい検索するとヒットするらしい。他のゲームは普通数十件程度らしい。
      また,ネタとしてハリウッド映画の中で最低点を叩き出したことについても触れられていました。寝たい方はおススメとも力説されておられました(笑)。
      講演の中で鉄拳のストーリー紹介するのに「いらすとやさん」に平八などの絵を買いえてもらったとかで,ショートでまとめていたのが面白かったです。
      あとは,実売の情報もばばーんと出ていたのが,何気にすごかったです。
      他の会社とかだと伏せるところが多いですが。
      販売の割合を円グラフで紹介していたのですが,ヨーロッパなどで51%1で,北南米で33%の売り上げで日本は3%程度しかないそうです。
      鉄拳はコミュニティを大切にして,育ててきたっていう話が結構印象深かったです。やっぱり昨今見ていると,勝手に育たんよなと,いくつかの失敗事例とかも見て感じますし,やっぱりやってくれるファンを大切にしないといけないよなって共感しました。
      あと,意外と背景作り込んでいて,開発費が高騰するという問題に触れられていました。これは別に鉄拳に限ったことではなく,すべてのゲームがその傾向があると思います。この問題は本当に死活問題なのですが,うまい解決案が個人的にも出せておらず,業界全体の課題ではないかと思います。一筋の光となるのはやはりAIを利用した開発支援かなとは思っているのですが,まだ各社模索中ではないかと思っています。逆にもう打開したよ!っていう事例があれば,CEDECなり公の場で是非発表していただきたいです。事例ができると一気に物事が進む可能性はあるじゃないかなと思っています。
      アーケードゲーム開発経験はさっぱりなので,ゲーセンで電圧下がって,CPUクロックが下がり,浮動小数点誤差が発生するとかの話は,やっぱりプログラマーなんですかね,興味深かったです。あとは,ヒット判定が円柱というのもほぇぇと思いました。押し出しは球でやっているらしい。
      開発に役立つかどうかは全く分からないのですが,話としては面白かったです。

      “GameSurvey”で開発・デバッグ効率が向上した取り込みAtoR from FINAL FANTASY XVII

      今日聞いた公演の中で最も実用的な内容でした。
      CEDEC 2017のゼルダのデバッグの紹介が本公演のきっかけだそうです。
      バグチケット対応とかやっていると,「それどうやってやるねん?」とか「カメラのどの向きで,該当オブジェクトどれやねん?」みたいな文章から再現しても全然わからんわ。…みたいな状況が開発しているとよく発生するので,いちいち確認するのにSlackでメンション飛ばしたり,それの応答待つために何回かやりとりしないといけないとか,結構無駄なやりとりが発生しがちなんですが,カメラ座標の他にカメラ方向などのいくつかの情報を即時に反映させられて,無駄なやり取り少なくなって開発効率向上したよって話です。断片的な情報で聞くと,プログラム的にはあんまり大した内容ではないのですが,こういう地味なのが一番開発には効くと思います。自分的にはかなり良い公演でした。
       雑にいうとURIでジャンプして該当箇所の位置・方向を合わせて看板表示もできるようにしたよっていう感じです。講演資料が後ほどあっぷされたらぜひ見てみると良いと思います。おススメです。

      プロシージャルゲームコンテンツ制作ブートキャンプ

      Houdiniを用いて,水などのシミュレーションをしましたって話でした。
      Blender等に比べるとやっぱりお高いだけあってパフォーマンスが良いなどのメリットがあるそうです。
      TAさんとかが好きな内容かもしれませんが,UEとか使っている環境だと簡単にインポートできるとかでいいのかもしれません。正直に絵的にもしょぼいし,あんまりテクニックを教えてくれるという感じの講演でもなかったので,スポンサーセッションだなという感じでした。

      リアルタイム光学エフェクトの深淵へ~究極表現への道と位置~

      幾何光学ベースから波動光学ベースに手法をアップデートして,ボケ表現をグレードアップしたという内容でした。1時間に収まらないため,ボケの話に急遽絞られたようです。
      事前知識は必須ですが,これまで川瀬さんの講演を追ってこられた方は,非常にわかる内容だったのではないかと思います。ペンシルマップ作成が、リアルタイムになっていたのが意外と驚きでした。オーサリングしやすそうでいいなぁと思いました。
      あんまり,詳しく解説できる技量がないので,興味ある方はCEDILのスライドを参照していただくのが良いと思います。

      『FINAL FANTASY VII REBIRTH』における会話イベントの量産とアニメーションワークフロー

      自分はUEを仕事で使っていない勢なのですが,製品に使われている内容だけあって,非常に参考になるものがありました。
      4秒・6秒とか結構細かい単位でモーションを区切っていて,会話の尺から近いものを採用するとか,一言目は裏読みするため,二言目からカメラが変わるとか,…そういうことだったのか。みたいな知見も得られて,良かったです。でも講演で結局,手動調整9:自動調整1の割合で,あんまりやっぱり自動化できないのかーと,それでもゼロから作るよりは格段にマシだと思いますが,なかなか全自動にするのはやっぱりできないんだろうなと痛感しました。


明日からCEDEC!

こんばんみん。Pocolです。

いよいよ明日からCEDECですね。
今年は前日入りしています。
所属会社も変わったこともあり,レポート提出しないといけないので,レポート提出用に明日からこちらにメモを残していこうかなと思います。

ちなみ、みなさんご存じかと思いますが…
CEDECの受講パスですが,バーコードに不備があり交換の必要があるそうです。
詳しくは下記の公式ページをご参照ください。
パス交換のお願いとお詫びのご連絡

新しい名刺が欲しい方は会場でお声がけください!
明日からの3日間楽しみです!
それでは、また。


レイトレ合宿の季節ですね!

こんばんわ、Pocolです。
レイトレ合宿の季節になりましたね。

そういえば,去年のレイトレ合宿については何も書いていないような気がしたので,書いておきます。
昨年の結果は下記ページから確認できます。
https://sites.google.com/view/rtcamp9

私の去年のテーマは「GGXで描画する!」というものでした。
完全な鏡面反射では面白みがないので,すこしグロッシーな反射を描画したい。…と思いレンダラーを書いてみました。

ソースコードは下記にあります。
ponzu


実装としては,DirectX RayTracingを用いています。
デノイザーはクロスバイラテラルフィルタを使う単純なものです。
アセットはKenny Blaster Kitを拝借しています。

プログラム作成はかなりトライアルアンドエラーを繰り返すので,昨年からシェーダリロードに対応するようにしました。
今年はもうひとつ進めてblinkを用いたC++側のホットリロード対応しようかなって思います。

PNG出力はやっぱりfpngが圧倒的に速いので,昨年も用いました。恐らく今年も用いると思います。
https://github.com/richgel999/fpng

シーンデータはFlatBuffersを利用しており,デシリアライズの時間が短くなるようにしています。
https://github.com/google/flatbuffers

さて,今年開催されるレイトレ合宿10ですが,ルールや実行環境は下記の通りだそうです。
https://github.com/shocker-0x15/rtcamp10_setup/blob/main/exec_env_spec.md
今年は,256秒で描画しないといけないそうです。出来るだけ無駄なくプログラムを組まないといけないですね。
今年の参加エントリーはもう締め切られているため,レイトレ合宿への参加に興味がある人は,日頃からレイトレのプログラム画像をX(旧Twitter)などにあげると良いかもしれません。

Wave組み込み命令を使ったテクニック

今週の,Graphics Programming Weekly Issue 347 – July 7th, 2024で,Wave組み込み命令を使ったテクニックの記事が記載されていました。
非常に参考になるので,目を通しておいた方が良いかと思います。
Compute shader wave intrinsics tricks

ライティングに関係するものはSIGGRAPH 2017 Advances in Real-time Rendering Courseの”Improved Culling for Tiled and Clustered Rendering”に記載されています。
ゲーム開発者であれば,とあるプラットフォームSDKのサンプルプログラムにも記述があるので見てみると良いかと思います。