Open3

HLSLシェーダーの魔導書まとめ

はるまろんはるまろん

1.2.1 メモリ転送

メモリへのデータ転送の方法として、PIO方式とDMA方式がある。

PIO(Programmed Input/Output)

CPUがコンピュータ内部の各デバイスとメインメモリ間のデータ転送する方法。
回路はシンプルになるが、CPUに負荷がかかり、転送中はCPUの処理が止まるため効率が悪い。

DMA(Direct Memory Access)

CPUを使わずに、バスを通じて周辺機能(アナログ機能、通信機能など)とメモリ間(フラッシュメモリ、ROM、RAM)のデータ転送を直接行う機能のこと

ハードウェアを用いているため、PIO方式より転送効率が高い。

https://jp.transcend-info.com/Support/FAQ-319
https://edn.itmedia.co.jp/edn/articles/1608/18/news015.html

はるまろんはるまろん

1.3.2 レンダリングパイプライン

描画結果を得るための手順をレンダリングパイプラインと言う(ざっくり過ぎ)

DirectX12のパイプライン(DirectX12の魔導書参照)

  1. Input Assembler(IA)
  2. Vertex Shader(VS)
  3. Hull Shader(HS)
  4. Tesselator(TS)
  5. Domain Shader(DS)
  6. Geometry Shader(GS)
  7. Rasterizer(RS)
  8. Pixel Shader(PS)
  9. Output-Merger(OM)

HLSL魔導書では、IA, VS, RS, PSについて触れられている。
実際出力する際の最小構成がこれなので、必要な処理ってことらしい。

IA, VS, RS, PSの詳細な流れ

  1. 入力アセンブラー(IA)
    メッシュの頂点バッファー, インデックスバッファー, プリミティブタイプなどの情報を必要なデータ形式として用意する。ドローコールが実行されると、GPUはグラフィックスメモリから必要なデータをレジスタに運ぶ。

頂点バッファー & インデックスバッファーの必要性については以下のサイトが分かりやすい。
http://marupeke296.com/DXG_No30_VertexIndexBuffer.html

そもそもなぜ三角形で分割するのかという点は、分割可能な最小の多角形であること、頂点をどのように動かしても、描画が破綻しないので扱いやすいからと言えると思う。


2. 頂点シェーダー(VS)
メッシュの頂点座標をスクリーン空間に変換する。
描画の出力先のスクリーンをビューポートとも言う。(範囲はx: -1 ~ 1 y: -1 ~ 1 z: 0 ~ 1)

基本的な手順

ローカル座標系 → ワールド座標系 → カメラ座標系 → スクリーン座標系


3. ラスタライザ(RS)
座標変換されたメッシュを塗りつぶすためのピクセルを決定する。
ほぼプログラマが関与する部分はないが、DirectXでは色々設定が用意されている。

D3D12_FILL_MODE_WIREFRAME: ワイヤーフレームで塗りつぶされる。
D3D12_FILL_MODE_SOLID: 面が塗りつぶされる。

カリング設定も用意されている。
D3D12_CULL_MODE_FRONT: ポリゴンの表側をカリング
D3D12_CULL_MODE_BACK: ポリゴンの裏側をカリング
D3D12_CULL_MODE_NONE: ポリゴンをカリングしない

基本的にゲームエンジンでは裏側をカリングしてますね。


4. ピクセルシェーダー(PS)
ラスタライザーで決められたピクセルのカラーを決定する。
陰影とか、ライティングを考慮したカラーを計算したりする。
色々とやることが多い。

主に頂点シェーダーとピクセルシェーダーをいじる機会が多いと思う。

はるまろんはるまろん

2.3 頂点シェーダー入門

魔導書を元に描いた頂点シェーダー

// 頂点シェーダーへの入力頂点構造体
struct VSInput
{
    float4 pos : POSITION;
};

// 頂点シェーダーの出力
struct VSOutput
{
    float4 pos : SV_POSITION;
};

// 頂点シェーダー
// 1. 引数は変換前の頂点情報
// 2. 戻り値は変換後の頂点情報
VSOutput VSMain(VSInput In)
{
    VSOutput vsOut = (VSOutput) 0;

    // step-1 入力された頂点座標を出力データに代入する
    vsOut.pos = In.pos;

    // step-2 入力された頂点座標を2倍に拡大する
    vsOut.pos.x *= 2.0f;
    vsOut.pos.y *= 2.0f;

    // step-3 入力されたX座標を1.5倍、Y座標を0.5倍にして出力
    vsOut.pos.x *= 1.5f;
    vsOut.pos.y *= 0.5f;

    return vsOut;
}

とてもシンプルでありがたい。
HLSLというかシェーダー全体として、基本的に入出力のデータ構造はユーザーが定義する。
また、どのメンバ変数が頂点 or UV or 法線...データなのかを定義するために、
: POSITION」のようなセマンティクスをつけてあげる必要がある。
https://learn.microsoft.com/ja-jp/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics

また、入力構造体は頂点の数分保持されていて、頂点バッファーという。


シェーダーファイルの拡張子.fxと.hlslの違いはなんだろう...?
調べた感じはあまり変わらなそう。