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のサンプルプログラムにも記述があるので見てみると良いかと思います。

コースティクスシェーダ

ShaderToyにいい実装があったので,自分なりにパラメータ調整したやつ。

// Based on: https://www.shadertoy.com/view/3d3yRj
// See also: KdotJPG's https://www.shadertoy.com/view/wlc3zr

float water_caustics(vec3 pos, float thickness) {
    vec4 n = snoise( pos );

    pos -= thickness*n.xyz;
    n = snoise( pos );

    pos -= thickness*n.xyz;
    n = snoise( pos );

    pos -= thickness*n.xyz;
    n = snoise( pos );
    return n.w;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 p = (-iResolution.xy + 2.0*fragCoord) / iResolution.y;

    // camera matrix
    vec3 ww = normalize(-vec3(0., 1., 0.8));
    vec3 uu = normalize(cross(ww, vec3(0., 1., 0.)));
    vec3 vv = normalize(cross(uu,ww));

    vec3 rd = p.x*uu + p.y*vv + 1.5*ww;	// view ray
    vec3 pos = -ww + rd*(ww.y/rd.y);	// raytrace plane
    
    pos.y = iTime*0.25;	// animate noise slice
    pos *= 3.;		// tiling frequency

    float w = mix(water_caustics(pos, 0.085), water_caustics(pos + 1.0, 0.085), 0.5);

    // noise [-1..+1] -> color
    float intensity = exp(w*2.5 - 1.);
    fragColor = vec4(vec3(intensity), 1.);
}

ちなみに元にしたコードは,https://www.shadertoy.com/view/ssfBDfにあります。

バジェット&パフォーマンス メモ

開発の為に各社のパフォーマンスバジェット数などのメモを残しておきます。
基本はPS5世代のオープンワールド関連で,メモは随時更新します。
また、ここに載っていないものでご存じのものがあれば、ぜひコメント等にて紹介して頂けると幸いです。

Cyberpunk 2077

・GDC 2023, “Building Night City: The Technology of Cyberpunk 2077”.
エンティティは15 millionなので,15 * 100万 = 1500万エンティティ。
ワールドセクターは256m単位。



Spider-Man 2

GDC 2024, “Applied Mesh Analysis: Automating Distant City LODs in Marvel’s Spider-Man 2”.

Hogwarts Legacy

GDC 2024, “Open World Rendering Techniques in ‘Hogwarts Legacy'”.
TODO : 後で追記。

Alan Wake2

GDC 2024, “Large Scale GPU-Based Skinning for Vegetation in Alan Wake”.





Call of Duty:MW2



God of War Ragnarok

GDC 2023, “Rendering “God of War: Ragnarok””.



Horizon Forbidden West

GDC 2023, “Space-Efficient Packaging For Horizon Forbidden West”.

GDC 2022, “Adventures with Deferred Texturing in Horizon Forbidden West”.

Ghost of Tsushima

GDC 2021, “Zen of Streaming: Building and Loading Ghost of Tsushima”.


Final Fantasy 16

CEDEC 2023, “Final Fantasy XVI:大規模ゲーム開発に向けて開発環境の取り組み”.

Skull and Bones

CEDEC 2023, “Skull and Bones カメラ依存のテクスチャストリーミング実例紹介”.


Tom Clancy’s The Division

GDC 2016, “Global Illumination in Tom Clancy’s The Division”.


接線空間データの圧縮について

知識というのは更新しないダメですね。
法線データのOctahedral Encodingをよく用いています。

※図は[Cigolle 2014]より引用。

ここで,Octahedron を45度回転させて,符号をビットを別に持てば,表現する面積領域が増えるから精度が倍に上がるのと,OctWrap()の演算も無くなるので,デコードも高速できるそうです。

これが,John White 3Dというブログの,”Signed Octahedron Normal Encoding”という記事にのっていました。これをみて「ほぇぇ~」と感心しました。頭いい!
ただ、符号ビットを別に持つの何かやだなーと思っていたのですが,R10G10B10A2なら10bit分が残っているので,この32bitでNormal, Tangent, Bitangentを表現しちゃいたいところです。

以前に書いたかもしれませんが,SIGGRAPH 2020のDOOMで使っている,接線空間表現を使おうかなと思っていました。

※図は[Geffroy 2020]より引用。

しかし、今日もっといいやつを見つけました。
Jeremy Ongさんの”Tangent Spaces and Diamond Encoding”というブログ記事。この中でも上記のDOOMの方式が紹介されていますが,もっといいのがダイヤモンドエンコーディングというやつ。

※図は[Ong 2023]より引用

ここでも,Octahedronの考えを利用しています。ちなみにサンプルコードは下記の様です。

※コードは[Ong 2023]より引用

ダイヤモンドエンコードで得られた接線を表すデータを残りの10bit部分に入れてしまえば,うまくR10G10B10A2に収めることができます。

ちなみに,Octahedron Encodingですが,Z+の半球が約束されているのであれば符号ビットもいらないので,下記のHemi-Octで実現できるそうです。

※コードは[Cigolle 2014]より引用

…ということで接線空間データの圧縮についてでした。

参考文献

  • [Cigolle 2014] Zina H. Cigolle, Sam Donow, Daniel Envangelakos, Michael Mara, Morgan McGuire, Quirin Meyer, “A Survey of Efficient Representations for Independent Unit Vectors”, Journal of Computer Graphics Techniques Vol.3, No.2, 2014.
  • [JohnWhite3D 2017] John White 3D, “Signed Octahedron Normal Encoding”, https://johnwhite3d.blogspot.com/2017/10/signed-octahedron-normal-encoding.html, 2017.
  • [Geffroy 2020] Jean Geffroy, Axel Gneiting, Yixin Wang, “Rendering The Hellscape of DOOM Eternal”, SIGGRAPH 2020 Advances in Real-Time Rendering course.
  • [Ong 2023] Jeremy Ong, “Tangent Spaces and Diamond Encoding”, https://www.jeremyong.com/graphics/2023/01/09/tangent-spaces-and-diamond-encoding/, 2023.

Screen Space Reflection関連の資料

実装用メモです。

SSRを実装すると,エッジ部分どうすんのよ?っていう問題が付きまとうのですが,下記の資料で解決方法の1つが示されています。
[Genin 2017] Remi Genin, “Screen Space Planer Reflections in Ghost Recon Wildlands”, https://remi-genin.github.io/posts/screen-space-planar-reflections-in-ghost-recon-wildlands/

SSRの実装自体は下記記事が参考になります。

また,ナイーブな実装だと,下に伸びるようなアーティファクトが発生することがあります。この問題への対策の一つとして,深度の厚みを考慮することによってある程度回避する方法が述べられています(Notes On Screen Space HIZ Tracing)。

また、上記のブログでは,深度によるアーティファクトの問題が指摘されていますが,GDC 2016のTHE RENDERING OF INSIDEの中で紹介されている隣接セルの深度を補間するのが良いでアイデアではないかと触れられています。

最適化については,“Screen Space Reflections in The Surge”の中で触れられているInterleave化を試すのが良いかもしれません。SSAOなどでは実際に実装してみた経験があり,かなり処理負荷削減できました。ただ、この手法はHi-Zには相性が悪いかもしれません。

色々と試して思うのは,HYBRID SCREEN-SPACE REFLECTIONSにあるように,近い点をSSRで解決してしまって,遠い箇所についてはレイトレしてしまった方が,良いかもしれないと思っていますが,実際にHi-Z使うよりもいい結果とパフォーマンスが得られるかどうかまでは試していないので,どこかで試してみるのはありかもしれないです。

また深度の厚みに関しては,”Screen Space Reflection Techniques”という文献の中で,Min-Max Hi-Zという手法が紹介されており,これを使うとパフォーマンスやアーティファクトの問題が解決できるかもしれないです。こちらもまだ試せていないです。

Recreating Naniteの記事

JGLRXAVPOKさんという方のブログで,Recreating Naniteという記事があります。
先日紹介した,MeshoptimizerとMEITZを使ってLODを生成するような話も載っています。

Recreating Nanite: The Plan
Recreating Nanite: Visibility buffer
Recreating Nanite: Cluster rendering
Recreating Nanite: LOD generation
Recreating Nanite: A basic material pass
Recreating Nanite: LOD generation -faster, better, simpler
Recreating Nanite: Runtime LOD selection
Recreating Nanite: Mesh shader time

FrameGraph関連の資料

実装用のネタ帳です。

● FrameGraph: Extensible Rendering Architecture in Frostbite
https://www.gdcvault.com/play/1024612/FrameGraph-Extensible-Rendering-Architecture-in

● Advanced Graphics Tech: Moving to DirectX12: Lessons Learned
https://www.gdcvault.com/play/1024656/Advanced-Graphics-Tech-Moving-to

● fg
https://github.com/acdemiralp/fg

● Why Talking About Render Graphs
https://logins.github.io/graphics/2021/05/31/RenderGraphs.html

● Task Graph Renderer At Activision
https://research.activision.com/publications/2023/06/Task-Graph-Renderer-at-Activision

● Organizing GPU Work with Directed Acyclic Graphs
https://levelup.gitconnected.com/organizing-gpu-work-with-directed-acyclic-graphs-f3fd5f2c2af3

● Render graphs and Vulkan – a deep dive
https://themaister.net/blog/2017/08/15/render-graphs-and-vulkan-a-deep-dive/

● Unreal Engine 5 – Render Dependency Graph
https://docs.unrealengine.com/5.3/en-US/render-dependency-graph-in-unreal-engine/

● GPU synchronization in Godot 4.3 is getting a major upgrade
https://godotengine.org/article/rendering-acyclic-graph/

● Render Graph 101
https://blog.traverseresearch.nl/render-graph-101-f42646255636

● An update to our Render Graph
https://blog.traverseresearch.nl/an-update-to-our-render-graph-17ca4154fd23

● EmbarkStudios kajiya-rg
https://github.com/EmbarkStudios/kajiya/tree/main/crates/lib/kajiya-rg

● Mastering Graphics Programming with Vulkan
https://github.com/PacktPublishing/Mastering-Graphics-Programming-with-Vulkan

● Render graphs
https://apoorvaj.io/render-graphs-1/

Virtual Texture関連の資料

Virtual Texture関連の私的メモです。

● Software Virtual Textures
https://mrelusive.com/publications/pubs_bytype.html
https://studiopixl.com/2022-04-27/sparse-virtual-textures

● Using Virtual Texturing to Handle Massive Texture Data
https://www.nvidia.com/content/GTC-2010/pdfs/2152_GTC2010.pdf

● Sparse Virtual Texturing
https://silverspaceship.com/src/svt/

● Advanced Virual Textures Topics
https://pdfs.semanticscholar.org/32f7/49c984e1ebd97e85f90d96b8ee5ed35c143a.pdf
https://dokumen.tips/documents/chapter02-mittring-advanced-virtual-texture-topics.html

● Virtual Texturing in Software and Hardware
https://mrelusive.com/publications/presentations/2012_siggraph/Virtual_Texturing_in_Software_and_Hardware_final.pdf

● Adaptive Virtual Texture Rendering in Far Cry 4
https://www.gdcvault.com/play/1021761/Adaptive-Virtual-Texture-Rendering-in

● Terrain Rendering in ‘Far Cry5’
https://www.gdcvault.com/play/1025480/Terrain-Rendering-in-Far-Cry

● Sampler Feedback
https://microsoft.github.io/DirectX-Specs/d3d/SamplerFeedback.html

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;
}