alphakaz’s blog

個人的備忘録

最新のGraphics情報を追うために Webサイト編

概要

「最新のグラフィックス技術を学ぶにはどうすればいいのだろう?」と思っても、身近に詳しい人がいなかったり、研究室で専門的に学んでいたりしない場合、なかなか最初の取っ掛かりを得るのは難しいと思います。

たとえばCG WORLDはとっつきやすいですが、アーティスト向けの情報が多いので、結局エンジニアリング的には詳細な情報が得られずに終わってしまったりもします。

今回はコンピュータグラフィックスに関する最新の情報を無料で追うことができるwebサイトをいくつか紹介します。

 

紹介

Advances in Real-Time Rendering

advances.realtimerendering.com

Siggraphという学会の、特にリアルタイムレンダリングに関する資料が公開されています。

話題になったゲームについて、どういった新技術を使ったか、どのような手法を応用したか、という情報を得ることができます。

Siggraphはコンピュータグラフィックスの周辺技術が発表される、歴史ある学会です。

 

GDC Vault

www.gdcvault.com

 

GDC(Game Developers Conference)は、名前の通り、ゲーム開発者のための学会です。

新作ゲームがここで発表されたりティザームービーが流れたりもするので、ゲーム開発に詳しくなくても、ゲーマーであれば名前を聞いたことがあるかもしれません。

GDC Vault は、GDCの講演資料が公開されるページです。有料会員にならないと見れない記事もあるので注意。

 

Cedil

cedil.cesa.or.jp

CEDECという日本で開催されるゲーム学会の資料が公開されるサイトです。

基本的に有料会員向けなので資料を見ることはできませんが、発表タイトルでググると発表をした会社が自前のホームページで資料を公開していたりするので、インデックスとして使えます。

 

Graphics Programming weekly 

www.jendrikillner.com

グラフィックスに関する論文や記事などを定期的に公開してくれるサイトです。

トピックの簡単なまとめつき。

 

日本語ブログ系

よくグラフィックスについて検索すると上位に結果が出てくる、日本語で書かれたブログです。英語の情報を見てもよくわからない! という場合や、情報が難解すぎて英語だと読み取れない…といったときに解説が書いてあったりして、よくお世話になります。

 

www.project-asura.com

shikihuiku.github.io

monsho.hatenablog.com

 

まとめ

ネットで拾える知識の多くは体系的ではない場合が多く、また英語の場合が多いため、初学の場合はこれらのウェブサイトを読み取るのも大変かもしれません。

以下に紹介する本は、DirectX12 の導入から基本的なリアルタイムレンダリングの知識を通して教えてくれるので、これからグラフィックスプログラミングを学びたいという初学者におすすめです。

 

Direct3D12 ゲームグラフィックス実践ガイド

DirectXの_SRGBフォーマットについて

はじめに

DirectX12のテクスチャフォーマットには、_SRGBというサフィックスがついたものがあります。

learn.microsoft.com

名前の通りSRGBガンマがかかった状態でデータを保存するフォーマットです。便利ですが、実際使ってみるといくつか引っかかりやすいポイントがあります。

 

重要なことは以下のページでほとんど書かれているので、このページはちょっとした躓きポイント的な部分を紹介できればと思います。

qiita.com

シェーダー上ではリニアな値

SRGBフォーマットで作られたテクスチャであろうと、シェーダでサンプルした際にはハードウェア側が自動でリニアの値に変換します。そのため、SRGBガンマがかかったRGB値を使いたい場合は、シェーダ上で自分で変換する必要があります。

逆に、*_SRGBのテクスチャをレンダーターゲットにして出力するときは自分でsRGB変換をする必要はありません。リニアの値を渡せばsRGBに自動で変換されます。

アルファブレンドはリニア空間で行われる

sRGB空間で保存されているわけなので、アルファブレンドが必要なポリゴンが描画されたときにも、sRGB空間でアルファブレンドが行われていることを期待するかもしれません。

が、それは間違いです。

*_SRGBフォーマットのテクスチャに半透明のピクセルを書き込むときは、

 

dest = srgbToLinear(RenderTarget.rgb)

src = (PixelShaderOut.rgb)

blended = alphaBlend(dest,src)

return linearToSrgb(blended) => テクスチャに書き込まれる

 

という経過を辿ります。

learn.microsoft.com

公式にも

When you use sRGB render targets, the runtime converts the render target color into linear space before it performs blending. The runtime converts the final blended value back into sRGB space before it saves the value back to the render target.

とあります。

 

これはSRGB空間でレイヤー間のアルファブレンドを行うPhotoshopのデフォルト挙動とは異なりますので、アーティストが作成した見た目と結果が異なるということがしばしば発生します。

 

じゃあどうするかというと、以下の回避策があります。

  • テクスチャフォーマットは*_SRGBのまま、RenderTargetViewのフォーマットを*_UNORMなどリニアなものにし、
  • シェーダでsRGBの値を書き出す

つまり「こいつはリニアなテクスチャだよ」とGPUを騙しつつ、SRGBフォーマットのテクスチャを渡すわけです。そうすると変換処理をスルーしてくれます。書き込み側のsRGBガンマ計算はシェーダで自分でやる必要がありますが。

ハードウェアの変換計算

ハードウェアが適用するsRGBガンマはIEC 61966-2-1 で定義されたsRGBガンマです。単純なcolor^1/2.2ではなく、分岐を含む正式な式です。

en.wikipedia.org

 

また、浮動小数点のピクセルシェーダ出力をUNORMに変換する際には、切り捨てではなくRound、つまり一番近い値に丸められます。

 

 

 

DirectXで余分にメモリを確保しないために

環境

DirectX12

対象者

DirectX12のチュートリアルを終えているが、GPUのリソース確保についてそれほど詳しくない人。

DirectX12のメモリアラインメントは基本64KB

DirectX12でリソースを確保する場合、いろんなチュートリアルや参考書にある通り、CreateCommittedResource()を使うのが一番手軽です。ヒープの確保とテクスチャ初期化をまとめて行ってくれます。

確かに楽です…が、ここでの落とし穴は、メモリのアラインメントが64KBであることです。つまり、64KB以下のメモリを確保しようとすると無駄が発生します。

 

たとえばR8の128x128テクスチャの大きさは単純計算すると16KBなので、これを素直にCreateCommittedResourceで確保しようとすると、48KBの隙間(パディング)が空きます。

隙間の空いたメモリ

たとえば、R8の128x128テクスチャを何も考えずに1000枚確保すると、16MBぐらいしか使っていないつもりでも実質64MBのメモリを消費しています。

自前でプロファイルをしているつもりでも、このアラインメントを考慮していない場合、潜在的にVRAMを大量に消費していたりします。

4KB Alignment

DirectXには、このアラインメントを4KBにする方法があります。これをうまく使えば、上のようなテクスチャを確保するときでも、無駄な隙間はなくなるはずです。

スッキリ詰められたメモリ

4KBにアラインメントされたテクスチャを確保する方法

この4KB Alignmentを使うにはいくつか条件があり、これらの条件をすべて満たす必要があります。

github.com

↑ CanUseSmallAlignment関数を参照

  • Bufferはダメ。Textureのみ
  • MSAAテクスチャ、レンダーターゲット、デプスステンシルはダメ
    • 特に、MSAA対応のテクスチャは4MB Alignmentになる
  • Texture2Dであること
  • テクスチャレイアウトがUNKNOWN
  • 4KB単位のタイルが16枚以下
    • R8の無圧縮テクスチャなら64x64などのサイズが1タイル

 

特に最後の条件はイメージしづらいと思うので、図示してみます。

例1-3

例4-6

VRAMは、整列した4KBを1タイルとします。ここからちょっとでもはみ出ると、はみ出た部分が1タイル扱いになり、タイルの枚数が16を超えると4KB Alignmentはできません。

4KB Alignmentのメモリを確保する

上記の条件を満たしているかチェックした上で、

  • D3D12_RESOURCE_DESC::Alignment = 4KBを指定する
  • CreatePlacedResourceでリソースを作成
    • CreateCommitedResource()はダメ。内部で暗黙的にヒープを一つ確保するが、このヒープの最低Alignmentが64KBだから
    •  

することで、ようやく4KBにAlignmentされたメモリを確保できます。

 

いや面倒

この判定自体は上記のリンクを参考にすれば可能ですが、まあ面倒です。
もっと簡単に、厳密に、スマートに判定をする方法はないのでしょうか?

あります。公式サンプルを見てみましょう。

github.com

D3D12SmallResources::CreateTextures()

textureDesc.Alignment=D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT

を指定した上でGetResourceAllocationInfo()に渡すと、そのtextureDescで可能なAlignment情報が返されます。*1

つまり、戻り値のinfoのAlignmentがSmallAlignmentに一致していれば、SmallAlignmentは可能というわけです。

なにせAPIに判定してもらっているのですから、安定したやり方です。

ですが、この方法には欠点があります。
デバッグレイヤーを有効にしたときに「4KB Alignment対応していないぞ」とエラーを吐きます*2

デバッグレイヤーを使ったデバッグ中にこのテクスチャ確保時のログが出続けたら邪魔で仕方がありません。一長一短です。

 

 

自分でやらずにアロケータを使う

そもそも、PlacedResourceを使って個人で汎用的なアロケータを作る事自体、コストが高いです。
車輪の再発明に無駄にカロリーを消費せず、素直にOSSのアロケータを使ったほうが無難でしょう。

ただ、上記の条件があることを知っているだけで、リソース設計に役立つと思います。

 

以下のOSSAMDが公開しているVRAMアロケータです。

CreateCommitedResourcesとほとんど同じインターフェースで、
4KBAlignmentも自動で判定してメモリを確保してくれます。

github.com

 

参考:

この記事に書いてあることはだいたい以下のリンク先に書いてあります。タイルについての具体的な話をしているページが見当たらなかったので、その部分について話したかった。

https://www.asawicki.info/news_1726_secrets_of_direct3d_12_resource_alignment

*1:GetResourceAllocationInfo()関数から戻ってきた値は、自前でVRAMの使用量をプロファイリングするのに役立ちます。実際に消費されているメモリ量が入っています。

*2:最新のAPIだと直っていてほしい

Unity2Dでピクセルの隙間をなくす

環境

Unity 2021.3.31f1

素材元

https://pipoya.net/sozai/assets/map-chip_tileset32/

問題

UnityのTilemapを使うと、ピクセル間に隙間ができることがあります。

この問題は、カメラの解像度に対してマップチップのサイズが合っていないために発生します。特にカメラ側の解像度が高いと問題になります。

 

カメラの解像度を自分でいい感じに調整するのも面倒ですし、マップチップのアセット側を調整したりアンチエイリアスを切るのも、それほどスマートな解決方法とはいえません。

また、軽くググって出てきたこれらの方法では、今回の問題は解決しませんでした。

 

解決策

こういうときはPixel Perfect Cameraを使います。

docs.unity3d.com

これはカメラの設定をピクセル単位で調整できるコンポーネントで、つまるところ、カメラの解像度がドットに一致するように簡単に調整することができます。

 

実践

PixelPerfectCameraコンポーネントをメインカメラにアタッチし、マップチップテクスチャのPixels Per Unit(1単位のピクセル数)と同じ数字(ここでは32)を、PixelPerfectCameraのAssets Pixels Per Unitに入れてみましょう。

GridコンポーネントやTilemapRendererコンポーネントの数字を操作していない限りは、以下のように隙間がなくなるはずです。