レイトレ日記 2022/07/27

今日は,ノートPCでの起動チェックを行いました。
で,早速DirectX周りでハマりました。

まず,当たり前ですがSwapChainの解像度がサポートされているものしか対応しないので,デスクトップと同じ解像度を指定したら,SwapChainが作成できなくて起動できないという問題にあたりました。
結局,画像出力用のレンダーターゲットを1つ用意して,そちらは高解像度にしてリードバックして,CPUからPNGに書き出し。SwapChainの方は,縮小して表示することにしました。

次のハマりポイントですが,EnumOutputs()がエラーを返すというのでハマりました。どうやらIDXGIAdapterの中には,EnumOutputs()の0番が失敗するものがいるようです。で,それを採用するように軽く実装したら,今度はAdapterが変わってDXRのチェックに通らないみたいなことが発生しました。結局,EnumOutputs()用にAdapterは別のものにするという対応で,ようやくサポート解像度がとれるようになりました。本当,こういうところがDirectXは面倒くさくて嫌いです。
コードは下記のような感じになりました。

    // DXGIアダプター生成.
    {
        RefPtr<IDXGIAdapter1> pAdapter;
        for(auto adapterId=0;
            DXGI_ERROR_NOT_FOUND != m_pFactory->EnumAdapterByGpuPreference(adapterId, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(pAdapter.GetAddress()));
            adapterId++)
        {
            DXGI_ADAPTER_DESC1 desc;
            auto hr = pAdapter->GetDesc1(&desc);
            if (FAILED(hr))
            { continue; }

            hr = D3D12CreateDevice(pAdapter.GetPtr(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr);
            if (SUCCEEDED(hr))
            {
                if (m_pAdapter.GetPtr() == nullptr)
                {
                    m_pAdapter = pAdapter.GetPtr();
                    m_pAdapter->AddRef();
                }

                RefPtr<IDXGIOutput> pOutput;
                hr = pAdapter->EnumOutputs(0, pOutput.GetAddress());
                if (FAILED(hr))
                { continue; }

                hr = pOutput->QueryInterface(IID_PPV_ARGS(m_pOutput.GetAddress()));
                if (SUCCEEDED(hr))
                { break; }
            }
        }
    }

これで,一応ノートPCでプログラムが起動するようになりました。
今週末が,レギュレーションテストなので,明日はテスト用のアセット生成などをして本番と同じフローになるように大枠だけは整えたいなと思っています。

レイトレ日記 2022/07/05

ここ、最近無気力感で何もやる気が起きないのですが,今日は頑張ってプログラムちろっと書きました。
土日で,再帰的にレイを飛ばすようにしたんですが,プログラムがバグっていて全部同じ色になってしまっていたので修正しました。マテリアルはまだ正式対応していないので,単色です。
まだ、マテリアル周りの実装をどうするか迷っています。

テスト実装で読み込んでいるモデルのロード処理がクソ重いということも分かったので,そろそろ本番用に最適化したやつに切り替えないとヤバそうです。
これから実装をバリバリ進められるように,シェーダのホットリロードにも対応しました。

残りの課題としては,

  • シーンデータの作成方法(ツールとかどうするか?)
  • トーンマップの実装
  • 本番シーンの選定
  • ライトの実装

などが,残っているので早い所これらは決めてしまいたいです。

残り57日!!

レイトレ日記 2022/06/16

今日は,シーン読み込みを実装することに。
毎回どうしようか悩んで,結局間に合わず同じものを使いまわすという過ちを繰り返してきたので,今回はちゃんとやることにした。

理想的にはバイナリ読み込んで,バイナリキャストして,そのまま使える形になるやつが欲しい!
…と思ったら既にありました。

FlatBuffers!!
https://github.com/google/flatbuffers

これよ。これ!欲しかったやつ。
早速試すと,かなり簡単にバイナリフォーマットを定義できる。
最初はstructとtableの違いが判らなかったが,プリミティブ型じゃないものを配列に取る場合はtableにしないとエラーになるので,自前定義のやつを配列で使いたい場合はtableにすればいいみたい。

結構便利なので,メッシュやらテクスチャやらの定義も書きまくる。

struct Vector2
{
    x:float;
    y:float;
}

struct Vector3
{
    x:float;
    y:float;
    z:float;
}

struct Vector4
{
    x:float;
    y:float;
    z:float;
    w:float;
}

struct Matrix3x4
{
    row0:Vector4;
    row1:Vector4;
    row2:Vector4;
}

struct ResVertex
{
    Position:Vector3;
    Normal:Vector3;
    Tangent:Vector3;
    TexCoord:Vector2;
}

table SubResource
{
    Width:uint;
    Height:uint;
    MipIndex:uint;
    Pitch:uint;
    SlicePitch:uint;
    Pixels:[byte];
}

table ResTexture
{
    Dimension:uint;
    Width:uint;
    Height:uint;
    Depth:uint;
    Format:uint;
    MipLevels:uint;
    SurfaceCount:uint;
    Option:uint;
    Resources:[SubResource];
}

struct ResMaterial
{
    BaseColor:uint;
    Normal:uint;
    Orm:uint;
    Emissive:uint;
}

table ResMesh
{
    MateiralId:uint;
    VertexCount:uint;
    IndexCount:uint;
    Vertices:[ResVertex];
    Indices:[uint];
}

struct ResInstance
{
    MeshIndex:uint;
    Transform:Matrix3x4;
}

table ResScene
{
    MeshCount:uint;
    InstanceCount:uint;
    TextureCount:uint;
    MaterialCount:uint;
    IblTexture:ResTexture;
    Meshes:[ResMesh];
    Instances:[ResInstance];
    Textures:[ResTexture];
    Materials:[ResMaterial];
}

root_type ResScene;

お作法的には,スネークケースで書かないと警告が出る。…が,そんな他人のルールなんて知ったこっちゃない。仕事じゃないんで。
自分さえよければいい。それが趣味プログラムの醍醐味!

ランタイム側はファイルを読み込んで,バイナリを

auto resScene = GetResScene(m_pBinary);

と引数で渡してやれば,データにアクセスできるようになる。すごく簡単!
ランタイムの読み込み側は実装したので,あとはシーンエディットと書き出しさえ実装すれば,パストレの実装に集中できそう。
シーンエディタ作るのめんどいなぁ。どうしよ?

残り75日!

レイトレ日記 2022/06/12

今日は,目標だった交差判定するところまでを実装した。

DXRでレイとヒットしたところが,赤くなる。
あとは,使うかわからんが,デノイズ処理用にアルベドと法線のデータも毎フレーム作成するようにした,デバッグビューで表示できるようにもしておいた。

あとは,マテリアルのデータや頂点データ等がちゃんとレイヒット時に取れるようになれば,パストレが組めそう。
で,そのためにはシーンデータの管理周りを整備しなくちゃなということで,実装に着手。シェーダ側は既に実装済み。

色々と進めると,作ったModelMgrクラスが根本的にダメなことがわかったので修正した。
HLSL側にでっかいVertexBufferとIndexBufferを渡して使う仕組みだったんだが,UINT32_MAX以上の数扱えないことに今更気が付いたという感じ。
結局,VertexBufferとIndexBufferは各メッシュごとに小分けに作ることにした。
HLSLはDynamicResourceで直接ResourceDescriptorHeapから頂点バッファとインデックスバッファを参照するように変更。
このために,DescriptorIndexを渡す必要があるので,

struct Instance
{
    uint32_t VertexBufferId;
    uint32_t IndexBufferId;
    uint32_t MaterialId;
};

という構造体を作って,これをバッファ化することに。このバッファにはInstanceID()を使ってアクセスする。
無理やりInstanceIdにマテリアルIDとジオメトリIDをパッキングする方法をやめたので,マテリアル数も少し多く扱えるようになった。
DynamicResource様様ですね。大分実装が楽になった。
結構めんどっちい実装なので,アドベントカレンダーのときに,ざっくりどんなつくりは説明しようかなって思っています。

残り79日!

レイトレ日記 2022/06/07

おとといは、会社の提出物関係で残業してしまったので,作業できず。
昨日は,マテリアルエディタのバグを修正した。

結局,定数バッファが設定されていなかったというしょぼバグ。
設計して,そのあとずっと実装を忘れていたんだろうな…。

あとはスキニング用の処理も中途半端な状態になっていて,ボーン行列が設定されない関係で計算がおかしくなっていたので,いったんスキニング処理をしないようにして対処。
時間が出来た時の楽しみとしてスキニング処理周りは残しておくことにする。
これで,スタティックメッシュについては問題なくマテリアルも表示できるようになったので,今夜はエクスポーターを書いて,いよいよレイトレ用のメッシュバイナリとマテリアルバイナリを作ることにする。
週末からはようやくレイトレできそうな予感。

残り84日!

レイトレ日記 2022/06/05

昨日はレイトレするためのメッシュとマテリアルを設定するためにマテリアルエディタの実装を進めていた。
まだ、何かバグってはいるもののジオメトリがそれっぽく読み込めるようになった。

一応だけども,日本語と英語の言語切り替えも対応しておいた。以前に実装した奴よりもシンプルな実装に出来て満足。

土日の目標だった,FBXとGLTFの読み込みは辛うじて達成。
マテリアル設定があやしいのとFBXの表示面がおかしい気がするので,今日対応出来たら対応しておく。
マテリアル定義はシェーダファイルから自動で作成されるように実装しているので,シェーダ書けば簡単に定義を追加することが可能。
シェーダ自体もホットリロードが利くように実装しているので,ガシガシ実装を進められる。

今日は,自前形式のメッシュとマテリアル形式に出力して,明日・明後日あたりから,DXRでレイトレするところまで進めたいなぁ…。

残り86日!

昨日は,ykozwさんが,レイトレアドベントカレンダーで「魔方陣、ガロア体、QMC」についての記事を公開してくれたようです。結構記事が長くてビビったのと,わかりやすく書かれていて,自分みたいな素人でも分かるのがいいですね。今回実装としてR2をサンプラーとして使おうかと思っていたので,こうした善し悪しが書かれている記事は大変有益で,本当に助かります。
こういう有益な記事が増えると,もっと国内で増えてくれると嬉しいですね。

レイトレ日記 2022/05/31

昨日は,とりあえずざっくりシェーダを書くところまで。
ルートシグニチャなどのレイアウト周りを決定した,Dynamic Resourceを使うことにしたので,ローカルルートシグニチャ要らずでスッキリした。
で,コンパイルしたが何かシェーダ周りで引っ掛かりまくる。
調べてみたら,何かWindows SDKのインストールが中途半端な状態になっていて,dxcompiler.dllとdxil.dllが同じディレクトリに存在しない状態になっていて,おかしなバイナリを吐き出す状態になっていた。
最新のWindows SDKをVisual Studio Installerから入れることで解決。
最新のWindows SDKを使ってもDynamic Resource周りでビルドエラーがでるので,Agility SDKを導入。
これで,ビルドが通ってアプリが立ち上がるところまで確認できた。

今日からは,マテリアルエディタ周りの実装に入って,DXR使ってメッシュを表示するためにメッシュのバイナリとマテリアルのバイナリを出力して,交差判定するところぐらいまで実装を奨めたい。
マテリアルエディタのメッシュローディングにassimp使っていたんだけども,メンテがめんどいのとFBXが正しく読み込めないとかの問題があることがわかったので,assimpは廃止しよう。
代りにtinygltfとFBX SDKとあと自前のOBJローダーを使って3ファイルだけサポートするように変更してしまおう。
ライトとかの設定もできるようにしたいところだなぁ…。

残り91日

レイトレ日記 2022/05/30

昨日というか,今日は,
論文ばっかり読んでいても実装して動くプログラムを作らなきゃいけないと思い始めて,とりあえずひな形を用意することに。
前回の合宿では,GPUレイトレする人が多かったので,今回はGPUレイトレすることに。
OptixやCUDAで組んでいもいいんですが,やっぱりゲームプログラマーならDXRよな!と思い,クソ使いづらいDXRベースでプログラムを組むことに。実行大臣から面倒くさいとの文句が出そうだが,「高い金払ってるんだ,きちんとやりやがれ!」と反論できるな,と思ったので,DXRで組むことにしました。実行大臣、期待してますよ。ファファファファファ…。

まずは,レギュレーションを満たさなければ何をやっても無と帰すので…
60FPSのアニメーションを10秒間で作れるようにするために,16ms以内に1フレームのレンダリング結果を出力できるようにするところから,実装をすることに。
で,前回の合宿とかはstb_image_writeとかを使っていたので,処理速度どんなもんだろ?と思ってデバッグビルドで処理速度を図ってみる。
3000[ms]から4000[ms]の間とのデバッグログが表示される。
「え?4秒?、ヤバくね?」
焦りまくる。簡単に済ませようとしていたのに,4秒? 16[ms]で終わらんやん。10秒でレンダリングとか無理ゲじゃね?
そう思い焦りまくる。
実際にウィンドウ上に表示されるFPSは7と表示されている。
「え?」ってなった。「勘違いではないぞ」
ヤバい、ヤバい、ヤバい、ヤバい、ヤバい。
これだと,またブービーまっしぐらだ,Sさんに馬鹿にされる。ヤバい、ヤバい、ヤバい。
せめてもう少しまともなプログラム書かねば。

ファイル出力がボトルネックとかヤバすぎる。
もっと早いpng出力は無いものか?
bmp出力ならフォーマットが分かっている分,如何様にもできるのだが,レギュレーションとしてpng or jpgというのが定まってしまっている。これは守らねばなるまい。
まぁ,png出力ライブラリなんてlibpng使った意外に書いた経験なんてないので,必死にググる。

…で,ググったところ fpng というライブラリにヒットした!
後で気づいたのだが,自分 star 押している。昔のオレ、ナイスぅ!
まぁ,早速速いということで当然組み込んでみたよね。
で,デバッグビルド測ってみても100倍ぐらい速くなっているぅぅぅ。
Releaseビルドで測ってみると5[ms]未満ぐらいで収まるが,5[ms]-28[ms]ぐらいで結構フレームによって不安定。
もうちょい何とかできんかなと思って,ソースコードを見てみると,std::vector()を使っていて,動的アロケーションが走るコードになっている。けしからん!本当にけしからん!
結局ソースコードを書き替えて,std::vector()を使うけども,最初の1回だけで無駄にアロケーションが走らないように,最大メモリで使いまわすコードに改変。微妙だけども,4ミリ秒ちょっとぐらいで,毎フレーム回るようになった。
とりあえず,これで16[ms]以内にファイル出力を毎フレームできるようになった。

つづいて,自動終了の対応。
60FPSで10秒のアニメーションだから,10秒で終わるようにコードを組んだ。
前回は,16[ms]以内にレンダリング終わらないプログラムだったので,監視用スレッドを立てて,そっちで一定時間たったら,stb_image_writeで出力みたいなコードにしていたんだけども,16[ms]以内できちんと収まるなら,別にスレッド立てるまでも無いかなとおもったので,今回はメインループで回るたびにチェックするプログラムにした,10秒経ったら,PostQuiteMessage(0)を投げて,アプリが終了する作りに。
これで,一応最低限のレギュレーションは守れるはず!!

で,落ち着いて合宿のレギュレーションを見直してみると
『フレームレートは10fpsから60fps、長さは3秒以上10秒以下のアニメーションを10分以内にレンダリングしてください。』

…あはは。
おじさん、60FPSで10秒以内でレンダリングしないといけないかと勘違いしてたよ。
10分ね。10分。
へへ、へへっ。

まぁ,高速化するに越したことはないよね。…と自分に言い聞かせた。
とりあえず,明日からナイーブならパストレぐらいは出来ないとダメかなと思ったので,ナイーブならパストレ実装することにします。
Ray Tracing Gems2 を見たら,Reference Path Tracingという記事があって,色々と参考になりそう。
RWTexture2D OutputはInterlockedAddできんやん!って思っていたら,Gems2の記事で

preColor = Output[uv];
Output[uv] = preColor + radiance;

と書いてあって,「あっ、なーほね!」って思いました。
頭全然回っとらんなぁーと思う,月曜日の夜でした。
まぁ、最下位脱出に向けて頑張ることにします。どうせ、上位勢には叶わんと思うので,下っ端は下っ端なりの頑張りを示せれば,それでいいかなと思っているので。
別に笑われたっていいんすよ。自分なりに精一杯,ちゃんと頑張れたなら…。
上位勢とは頭の良し悪しも違うし,キャリアも違うし。へぼはへぼなりに頑張れればそれでいいかなと思うんです。

残り93日。