1-Phase Occlusion Culling

完全に私的な実装メモです。
通常,オクルージョンカリングを実装する際は,GPU Zenやもんしょさんのサイトに載っているように2-Phaseでジオメトリ描画して実装するのが普通かと思います。
少し前ですが,GDC 2021の“Samurai Landscapes: Building and Rendering Tsushima Island on PS4”というセッションの,43:45あたりから,Occlusion-Cullingについての説明があり,Ghost of Tsushimaの実装では,前フレームの深度バッファと,それらから保守的(conservative)に作成したミップレベル,エピポラーサーチを用いて,現在フレームの深度バッファを復元し,1回のジオメトリ描画でオクルージョンカリングを実装する方法が紹介されています。
説明が分かりやすいので,アルゴリズムについては元動画を参照してください。
馬鹿まじめに線形探索をせずに,ミップマップを使って検索するのがアルゴリズムのキモみたいです。

一応実装コードが紹介されていますが,動画の品質が低すぎて全然良く見えませんw。
そこで,それっぽい感じに見えるコードを自分で推測しながら書いてみました。
推測で書いているのと,きちんと動作検証もしていないので,バグっている可能性があるので,まんまコピペで使用して不都合・不利益が発生しても何ら責任は負いませんのでご注意ください。
もし、「ここはこうじゃね?」とかアドバイスあればコメントください。

Depth Reprojection

// Forward-project last frame's depth to this frame's space
{
    float zPrev = pSrt->m_texIn[dispatchThreadId.xy];
    float zDepthPrev = DepthFromWorld(zPrev, pSrt->m_vecRecoryHypebolicDepth);

    float4 posClipPrev = float4(VecClipFromUv(uvThread), zDepthPrev, 1.0f);
    posClipPrev *= zPrev; // This ensure that zCur will end up in posClip.w;

    float4 posClip = mul(posClipPrev, pSrt->m_matClipPrevToClipCur);
    posClip.xyz /= posClip.w;

    float2 uv = UvFromVecClip(posClip.xy);

    if (all(uv >= pSrc->m_rectScissor.xy) && all(uv >= pSrc->m_rectScissor.zw))
    {
        // NOTE : z ends up in posClip.w so we can use that directly.
        float z = posClip.w;
        
        if (z < pSrt->m_zNear)
            z = pSrt->m_zFar;

        // NOTE : Add a small bias to resolve self-occlusion artifacts
        z += pSrt->m_dsBias;

        if (z > pSrt->m_zFar || zPrev >= pSrt->m_zFar)
            z = pSrt->m_zFar;

        float2 xy = floor(uv * pSrt->m_texture.m_dXy); // 多分、テクスチャサイズを乗算してウィンドウサイズに戻している。
  
        AtomicMax(pSrt->m_texOut[xy], z);
    }
}

{
     float4 posClip = float4(VecClipFromUv(uvThread), 0.0f, 1.0f);

     float4 posClipPrev = mul(posClip, pSrt->matClipCurToClipPrev);

     if (any(abs(posClipPrev.xy) >= abs(posClipPrev.w))
     {
          float2 uv = UvFromVecClip(posClipPrev.xy / posClipPrev.w);

          float z = pSrt->m_texIn->SampleLOD(pSrt->m_sampler, uv, 0) * pSrt->m_dsBias;

          AtomicMax(pSrt->m_texOut[dispatchThreadId.xy], z);
     }
}

Hole-Filling

float2 uv = (dispatchThreadId.xy + 0.5f.xx) * pSrt->m_texture.m_div;
float zMax = pSrt->m_texIn.SampleLOD(pSrt->m_sampler, uv, 0);

if (zMax == -FLT_MAX)
{
    float2 normalEpipole = normalize(pSrt->m_uvEpipole * uv);
    float2 dUvStep = pSrt->m_texture.m_div * normalEpipole;

    for(uint iMip = 1; iMip <= pSrt->m_iMipLast; ++iMip)
    {
        float2 uvSearch = uv - dUvStep;
        dUvStep *= 2.0;

        float z = pSrt->m_texIn.SampleLOD(pSrt->m_sampler, uvSearch, iMip);

        if (z != -FLT_MAX)
        {
            z = max(z, pSrt->m_texOut[dispatchThreadId.xy]);
            z *= pSrt->m_dsBias;
            z = min(z, pSrt->m_zFar);
            pSrt->m_texOut[dispatchThreadId.xy] = z;

            return;
        }
    }

    if (zMax < pSrt->m_zNear)
        zMax = pSrt->m_zFar;

    zMax = min(zMax, pSrt->m_zFar);

    pSrt->m_texOut[dispatchThreadId.xy] = zMax;
}

GPUで実行するテレイン上へのプロシージャルな配置の話

先日、Twitter(現X)を見ていたら,Unity engine上でテレイン上に配置する奴のツイート(リポスト)が来たので,忘れないようにメモとして残しておこうと思います。

1つ目は,GPU Run-time Procedural Placement on Terrain というやつです。
Horizon Zero-Dawnの資料を元に実装しているっぽく、どこぞの会社とおなじようなものっぽいです。

2つ目は、上記記事の続編と思われる Efficient GPU Rendering for Dynamic Instances in Game Development です。

いわゆるテレインを使用するオープンワールドゲームを作る際には役に立つかと思います。

premake5覚書

こんねね~。Pocolです。
premake5の覚書をしておこうと思います。

XBox系のプラットフォームの場合は,Billzard社がpremake-consolesというGithubリポジトリを公開しているので,これを使えばよさそうです。
https://github.com/Blizzard/premake-consoles

で,青いプラットフォームの場合は,これまたBillzard社がソースコードをフォーラムにて公開しているので,それを使えばいいです。

問題はこれらが使えない場合です。既にpremake作っちゃっているやつとか,これから出てくる新規プラットフォームの場合とかですね。
自力で実装する必要があります。ほとんどの場合はSDKがC++だと思われるので,C++のコンパイルは問題なく対応できるでしょう。
じゃ、何が問題か?
そうです。前回やったシェーダ設定ですね。恐らく問題になるでしょう。
SDKでVS拡張が提供されている場合は,プロジェクトファイルに記述すればいいので,premakeを拡張していけばいいです。

やり方としては,
https://github.com/premake/premake-core/blob/master/modules/vstudio/vs2010_vcxproj.lua
のFxCompileおよびfxCompileで記載されている箇所を参考にします。

premake用に公開するAPIは,
https://github.com/premake/premake-core/blob/master/modules/vstudio/_preload.lua
のshadertypeとか,shadermodel,shaderincludedirsあたりを参考にLuaに実装を付け足します。

先ほど言った、VS拡張が提供されている場合はこんな感じでいいのですが,じゃ、提供されていない場合はどうすればいいでしょうか?
残念ながら,自分で作るしかないですね。

作り方については,以前に書いた「カスタムビルドルール!」「Visual Studioと格闘中…。」の記事で紹介しているので,これを参考に実装すれば良いでしょう。

…ということでコンソールプラットフォーム用のpremakeのシェーダビルド対応について,覚書を書いてみました。
殆どの人には,全く役に立たないんですが,自分だけには役立つというしょうもない記事でした。

premake5でシェーダ設定する

こんこよー。Pocolです。

ちと分け合って,premake5でプロジェクトを作成する際に,シェーダのコンパイル設定を含めてしまいたいな~と思いました。
premake5からHLSLがサポートされているようなので,実際に書いてみました。
リポジトリは下記です。
https://github.com/ProjectAsura/premake_test

プロジェクトを生成するluaは下記のような感じで書きます。

local project_dir="project"
location(project_dir)

-- ソリューション名.
workspace "SampleSolution"
    -- 構成名.
    configurations { "Debug", "Develop", "Release" }

-- プロジェクト名.
project "SampleProject"

    kind "ConsoleApp"
    language "C++"

    -- 出力ディレクトリ.
    targetdir "bin/%{cfg.buildcfg}"

    -- インクルードディレクトリ.
    includedirs { "include/**" }
    shaderincludedirs { "$(ProjectDir)../res/shader" }

    -- 対称ファイル
    files { "**.h", "**.cpp", "**.hlsl", "**.hlsli" }

    -- シェーダモデルとエントリー名設定.
    shadermodel "6.5"
    shaderentry "main"

    -- ヘッダ変数名とヘッダーファイル名設定.
    shadervariablename "%%(Filename)"
    shaderheaderfileoutput "$(ProjectDir)../res/shader/Compiled/%%(Filename).inc"

    -- シェーダヘッダファイル設定.
    filter { "files:**.hlsli" }
        flags "ExcludeFromBuild"

    -- 頂点シェーダ設定.
    filter { "files:**_vs.hlsl" }
        shadertype "Vertex"

    -- ピクセルシェーダ設定.
    filter { "files:**_ps.hlsl" }
        shadertype "Pixel"

    -- コンピュートシェーダ設定.
    filter { "files:**_cs.hlsl" }
        shadertype "Compute"

    -- ジオメトリシェーダ設定.
    filter { "files:**_gs.hlsl" }
        shadertype "Geometry"

    -- ドメインシェーダ設定.
    filter { "files:**_ds.hlsl" }
        shadertype "Domain"

    -- ハルシェーダ設定.
    filter { "files:**_hs.hlsl" }
        shadertype "Hull"

    -- 増幅シェーダ設定.
    filter { "files:**_as.hlsl" }
        shadertype "Amplification"

    -- メッシュシェーダ設定.
    filter { "files:**_ms.hlsl" }
        shadertype "Mesh"

    -- 構成設定.
    filter { "configurations:Debug" }
        defines {"DEBUG"}
        shaderdefines {"DEBUG=1"}
        symbols "On"
    filter { "configurations:Develop" }
        defines {"NDEBUG"}
        shaderdefines {"NDEBUG=1"}
        optimize "On"
        symbols "On"
    filter { "configurations:Release" }
        defines {"NDEBUG"}
        shaderdefines {"NDEBUG=1"}
        optimize "On"

このluaファイルを使って,サンプルのリポジトリで下記のコマンドで,プロジェクトを生成します。

.\premake5.exe vs2022 --file=my_premake.lua

プロジェクトを生成すると projectフォルダが出来上がり,そこにソリューションファイルが作成されます。ソリューションファイルを開くと次の画像のような感じになります。

シェーダファイルのプロパティを見ると次のように設定されているはずです。

これで,シェーダのビルドが出来るようになるはずです。
下記のPremakeドキュメントに設定できる項目が記載されているので,より細かい設定をしたい方はドキュメントを参照すると良いと思います。

…ということで,premake5でのシェーダ設定方法について紹介してみました!

サンプリングの参考資料。

Twitterみていたら,サンプリングの参考になりそうなツイートを見かけました。
SSAOとかの実装で使っていきたいなと思いました。

画像に書いてあるサイトは下記の様です。
https://note.com/outburst/n/n631a3845186c

ReSTIRがサッパリ解からない。

この記事はレイトレ合宿9のアドベントカレンダー2023 7/13の担当分の記事です。

こんにちわ,Pocolです。
最近,ReSTIRを調べているのですが,未だにサッパリ解かりません(特にGRIS周辺)。

今回は,自分の理解を促すために自分なりにまとめたスライドを作ってみました!
しかし,内容をサッパリ理解していない人が作っているスライドなので内容が盛大に間違えている場合があります。
間違いがあった場合は,間違い箇所の指摘とその修正例を教えて頂けると大変ありがたいです。あるいは,「そういう説明よりもこういう説明の方がいいよ」とか「この項目の説明抜けてる!」とか,アドバイスも貰えると嬉しいです。
または,有識者が分かりやすい解説記事を書いてもらえると大変助かります。
あと,論文の内容を切り貼りしている関係で,数式の表記ゆれがあります。予め注意してください。

読み下ろしテクニックの話。

こんらみ。Pocolです。
自分は,駿台っ子なんですが,当時御茶ノ水校や千葉校,柏校などで英語長文を担当していた新井和泰先生の教え子でもあるのですが,
この歳になっても非常に役に立っているのが,新井先生から習った読み下ろしテクニックです。新井先生!滅茶苦茶役に立っています!!

 こちらは一応文献的には,駿台フォーラム第18号で紹介されています。恐らく絶版していると思われるので,今手に入ることはないかもしれません。

 で,ネットで検索していたら,訳下しという記事が出てきて,当時先生が言っていたような話がヒットしました。なんだか懐かしい気がしてきたので,時間を掛けて読んでみようかなって思います。

https://thbook.simul.co.jp/archive/category/同時通訳

先生が今だに現役だったら申し訳ないんですが,恐らくもう還暦を超えていて,営業妨害にはならないと思われるのと,非常に有用なテクニックなので,時間があればどこかで先生のテクニックを紹介したいと思います。まぁ興味ある人がいればですけども…。