LIFE LOG(MochiMochi3D)

MochiMochi3D

3D関係の記事を書きます

レイトレ合宿8に参加しました

レイトレ合宿8に参加してきました!参加レポートとしてレンダラー開発記と合宿の様子や感想を記事にまとめました!

レイトレ合宿への参加

私は去年ぐらいからレイトレをやっており、ちょこちょこレイトレ業界の人が集まるレイトレ合宿なる集まりがあることを聞いていました。レイトレ合宿に参加したことがある人から「参加する人みんなヤバい人だよ」と聞かされておりいつか参加して話を聞いてみたいなぁとずっと思っていましたのですが、ご時世のこともありレイトレ合宿は去年、一昨年と開催されることはなく学生の間は参加できないかもなぁとあまり希望は持てていませんでした。

そんな中、今年の5月頃になんと知り合いの方から今年レイトレ合宿が開催されると連絡され、しかも参加させていただけることになりました!(すごくうれしい)

開催時は台風が直撃するなど中々大変な状況でありましたが、何とか無事にレイトレ合宿に参加することができました。

ということでレイトレ合宿に参加した話を記事としてまとめとこうというのがこの記事です。

5月~ 6月

SDKの選択

5月頃にレイトレ合宿の連絡が来てよっしゃレンダラーを作るぞ!ということで息巻いていたのですが、今年はなんとレギュレーションがアニメーションレンダリングのみということでした。凝ったことやろうと思うと必然的にGPU&デノイザーの実装が不可欠になるため、CPUレンダラーしか書いたことがなかった私は当時絶望してました。

というわけで急いでGPUレンダラーについて学んで実装してみようとしていました。GPUでレイトレをするとなると、GPUレイトレAPIを扱わなければなりません。メジャーなGPUレイトレAPIと言えばOptiX, DirectX 12, Vulkan Raytracingの三種類が挙げられます。

Vulkanは最近話題のグラフィックスAPIであり、他に比べてかなりデバイスに近い部分の設定を明示的に書くことができるAPIです。当初はせっかくだしなんか最近耳にするしVulkan Raytracingをやってみようと触ってみたのですが、明らかにGPUのGすら知らない初心者が触るには低レイヤーすぎてラスタライズでTriangleを出したとこまでやって断念しました。(しかも情報がびっくりするぐらいなんもない。)

結局、VulkanではなくOptiXを使用することにしました。OptiXはnvidiaが出してるレイトレ専用のSDKで、GPUレイトレをするなら結構これが選ばれている印象があります。Vulkanに比べればかなり情報があり、レイトレアドベントカレンダーでも記事を書いている人がいましたのでOptiXを使っていくことを決めました。今思うとかなり扱いやすくCUDAもC++と同じように書けたので扱いやすさで言ったらこれが一番と感じます。

www.media.lab.uec.ac.jp

qiita.com

OptiX開発

OptiXはCUDAを使用してレイトレーシングプログラムを書くことができるAPIで、DirectXやVulkanとか異なり完全にレイトレ専用です。なので他のAPIに比べるとレイトレをする上ではかなり簡潔でCUDAがほぼC++と同じ書き方でできるのでGPUレイトレでは一番扱いやすいかなと感じました。

OptiXの環境設定は初めてだったので上記のsketchbooksさんの記事を元にSDKsampleをbuildする形でやっていきました。OptiXのSDKにはいくつかサンプルプログラムが含まれており、一番シンプルなものとして1つのTriangleを出すだけのoptixTriangleがあります。このサンプルは最低限OptiXでレイトレするのに必要なプログラムが書いており、全体像を把握するうえではわかりやすかったです。最初の内はこれを読みながら書いていき、これを改造するような形でレンダラーを構築していきました。

OptiXの情報収集は基本的にPrograming GuideとSDKサンプル、sketchbooksさんの記事、時々nvidiaのフォーラムを見ながらという感じで行っていきました。また、ちょこちょこ連絡を取っていたyumcyawizさんから教えて頂きました(というかほとんどyumcyawizさんからの情報)。

raytracing-docs.nvidia.com

そうゆうわけで色々と実装していきました。

頂点情報とかの扱い

OptiXではCUDA上で使える関数が結構あり、当たったGeometryの法線や頂点などを取得するものもあります。

頂点情報とかはこれで取得すればいいじゃんという感じはありますが、頂点のShading NormalやUVの情報をOptiXに送ったり取得することができないっぽいし、後々のほかの実装を考えるとユーザーデータとして頂点情報は渡しておく必要があると思います。

CPUの配列など任意のデータはcudaMemcpyによってユーザーデータとしてGPU側に送ることができ、頂点のデータはこれによって送る形で実装しました(exampleでもこんな感じでした)

実際、光源サンプリングやAnimationなどを考えるとこうした感じで実装しなければいけないような気がします。

7月

Texture

OptiXではCUDAによってShaderが書かれるわけなのでCUDAの機能を使うことができます。TextureについてはCUDAにはTexuture用のcudaTextureObjectと呼ばれる構造体があり、各ピクセル間の補完など色々な設定をすることでCUDA上でTextureを簡単に扱うことができます(あと効率がいいらしい)。基本的には以下のサイトを見れば実装することができます。

docs.nvidia.com

Normal mapやRoughness mapなどのsRGB変換のON,OFFし忘れについてはバグの原因になるので注意

レイトレアドベントカレンダー

レイトレ合宿では毎週誰かが記事を書くレイトレアドベントカレンダーというものがあり、私は7/17を担当していました。ちょっと前にIBLの重点的サンプリングを実装して、まとめておきたいなーと以前から思っていてちょうどよかったのでアドベントカレンダーの記事はこれを書きました。

kinakomoti321.hatenablog.com

また、当初レンダラーに組み込もうと考えていたManifoldNEEという手法について調べていた時期であり、論文も大まかにはわかってきたからついでだしもう一個作っちゃおっということでこれらをまとめた記事も投稿しました。

kinakomoti321.hatenablog.com

GGX

そろそろLambert以外にもBRDFを追加しようとGGXを確かこの頃に実装しました。CPUレンダラーでそこそこGGXは実装していたので、大まかな実装はすぐにやれたのですが中々微妙なバグを出してしまい、それの対応で結構時間がかかりました。

そのバグというのは謎の黒点が生じるというバグでした。

黒点が生じるのはGGXを実装しているとよくあるバグで大抵D項かG項の三角関数でNanが出てくることが原因です。多分原因はそれだろうなーとは思っていたのですが、どれが悪さしているのか判別するのに苦戦していました。

そんな中、ShockerさんからCUDAもC++同様に標準入力printfやNanのチェック用関数isnan,isinfなどが使えるということを教えて頂き(当時全く知らなかった)、どうにかして対応することができました。ちなみにどこがバグ起こしてたかは結局わからずNanが輝度に紛れ込んでいたら破棄するという処理をisnanを使って書くことで対応しました(他の原因でNanが出るかもしれないので輝度の加算時にチェックを行う方が安定するかも)。

その後、yumcyawizさんより面白いことに三角関数を使用しないGGXの式が以下の論文にあるとSampling the GGX Distribution of Visible Normalsという論文を教えて頂きました。ほんとにこれで大丈夫なのかと半信半疑ではあったのですが、実際に実装してみたところちゃんと等価なことが確認できました。この方法ならNanが出ることはないので今後こちらを採用した方がよさそうです。

Heitzの手法と従来の手法の比較 全く違いはなし

https://jcgt.org/published/0007/04/01/paper.pdf

最終的には可視法線サンプリング&Heitzの手法でGGXを実装しました。

Disney BRDFの実装

今回はより実用的なレンダラーを目指して、去年実装を断念したDisney BRDFを実装することを決めました。

Disney BRDFとはDisneyが2012年に発表した幅広い表現が可能なBRDFです。これはいくつかのBRDFを組み合わせた複合BRDFで、直感的なパラメーターをいじることで(透明じゃない)現実の大体の質感を表現することができ、今のPBRマテリアルの原型になっているものです。

Physically-Based Shading at Disney より引用

DisneyBRDFの式はshockerさんが記事にまとめており、実装は基本的にこの式を愚直に実装したという感じで行いました。

[https://rayspace.xyz/CG/contents/Disney_principled_BRDF/#[Burley2012]:embed:cite]

式そのものはそこまで難しい物ではないのですが、一方でサンプリングに関しては色々と難しい問題があります。

Disney BRDFはDiffuse, Specular, Sheenなど複数のBRDFを単に加算するという形でBRDFを構成しています。それぞれのBRDFは特有の分布をしており、これらにはそれぞれ適切なサンプリング方法を考えることができます。しかし、これらが組み合わさったDisneyBRDF全体をうまく重点的サンプリングする方法を解析的に求めることはできません。

代わりに各BRDFのサンプリング方法をどれか1つ使えば、少なくともそのBRDFがある以上ある程度はDisney BRDFに沿った重点的サンプリングとして扱うことが可能になります。しかし、これには問題があり、選択したサンプリング手法が他のBRDFには不適切であるということが挙げられます。

分かりやすい例としてはDiffuseとSpecularの関係です。Diffuseは(大体)一様に散乱する形で表されますが、一方でSpecularというのは反射方向に強く現れる指向性の強い分布を持ちます。このようなDiffuseとSpecularの分布の大きな差異によって、DiffuseのCosine重点的サンプリングをそのままSpecularのサンプリング方法として使用すると当然ながらノイズがすごいことになります。

Roughnessが低いSpecularに対し Cosine重点的サンプリングを適用した場合

従って、どれか1つのBRDFのサンプリング方法を使い続けるという手法は複合BRDFには全く歯が立ちません。

じゃあどうするかというと、Multiple Importance Ssamplingを使います。MISは複数のサンプリング方法をうまく組み合わせる方法で、どれか1つを確率的に選択するOne sample Modelを使用することでこの複合BRDFのサンプリングを行うことができます。

やり方としては単純で、各サンプル方法に選択される確率 c_1,c_2,c_3,...,c_nを与え、それに従って確率的にサンプリング方法を選んでそれでサンプルするだけです。その際の確率密度はMIS weightがかかりますが、i番目のサンプリングが選択されたとするとそのMIS weightはサンプリング方向 \vec{\omega}の各サンプリング方法の確率密度 p_1(\vec{\omega}),p_2(\vec{\omega}),p_3(\vec{\omega}),...,p_n(\vec{\omega})を用いて

 \displaystyle{Weight = \frac{c_i p_i(\vec{\omega})}{c_1 p_1(\vec{\omega}) + c_2 p_2(\vec{\omega}) + c_3 p_3(\vec{\omega}) + ... + c_n p_n(\vec{\omega})}}


と表されます。MIS weightとpdfを約分すれば、事実上のpdfの値は 

 \displaystyle{pdf =c_1 p_1(\vec{\omega}) + c_2 p_2(\vec{\omega}) + c_3 p_3(\vec{\omega}) + ... + c_n p_n(\vec{\omega})}


と書くことができます。

私の実装の流れは大体こんな感じです

  1. DiffuseとSpecularのそれぞれのウェイトを決める
  2. ウェイトを元に選択する確率[tex: c{diffuse}, c{specular}]を決定する
  3. その確率でどっちを選ぶかを選択、サンプルを求める。
  4. もう片方でそのサンプルした方向のpdfを求め、全体のpdfを求める。

サンプリング方法についてですが、SheenやSubsurfaceはほとんどDiffuseのサンプリングで問題なく、Diffuse or Specularでサンプル方法を決定しています(ほんとであればClearcoatも入れた方がいい)。

こうしてDisney BRDFのサンプリング方法ができたわけなのですが、まだ効率化できる部分があります。手順1.のウェイト決定ですが、これは単純に寄与が高くなるBRDFを取れるようなウェイト付けをしてあげればより効率が良いサンプリング方法になります。

ですが、寄与が高くなるやつを取ればいいじゃんと言っても、出射ベクトル \omega_oがサンプリングによって得られて初めて寄与が得られるため、選択時に寄与がどうなるのかというのははっきりとはわからないわけです。選択時にあるのは入射ベクトル \omega_iの情報だけで、ここからおおよそ大体寄与が高くなる可能性が高いかどうか推測する形でウェイトを決めていくわけです。

簡単な例としては、DiffuseはMetallicが1の時は完全に取り除かれるため、(1 - Metallic)という係数をウェイトにつけてあげることでMetallicが高くなるほどDiffuseが選ばれなくするということができます。私の実装だとDiffuse = (1 - Metallic), Specular = 1という形でウェイトを決めており、結構おおざっぱです(それでもそこそこいい感じになる)。

レイトレ合宿でこれについて聞いてみたところ、実用的なものにするにはフレネルの値を使用する(Specularはフレネルに依存するので)、 \omega_iを固定し \omega_o積分した値を事前計算し (UE4のEnviroment BRDFのような感じ?)、 \omega_iが決まった時点でその値を使い寄与を推測するといった方法が挙げられるらしいです(あんまり理解できてない)。もう少し探ってみても面白そう

https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf

最後にMISで必要になる与えられた方向からpdfを算出する処理を書きますが、先ほど示した実質のpdfを書けば問題ありません。

こんな感じでDisney BRDFを実装しました。結構やりたかったことなのでちゃんとできてうれしかったです。

上からSubsurface, Metallic, Roughness, Sheen, Clearcoat (Subsurfaceはもしかしたら全て0かも)

8月

GLTF

いよいよ夏休みに入り、レンダラー開発も佳境に入ってきました。そろそろこのあたりでモデルのフォーマットどうしようかなぁと考え初め、今回はAnimation Renderingということでアニメーションを含むことができるモデルフォーマットとしてGLTFを採用しました。

GLTFはKhoronos Groupが開発しているモデルフォーマットでJSON形式のgltf,binary形式のglbの2種類の形で保存されます。モデル情報はもちろん、Metallic&RoughnessといったPBR Material、Animationなど幅広い情報を持つことができ、比較的シンプルな構造をしているので一番最初にAnimationを含めたフォーマットとしてはこれが一番楽そうということでGLTFを使っていきました。

GLTF loader

GLTFの読み込みにはtinygltfを使用しました。このローダーはGLTFの中身をほぼそのまま渡してくれるような感じでいろいろと扱いやすかったです。

github.com

GLTFはnodes(オブジェクト)、material, animationなどそれぞれで記録されて、お互いをインデックスで指定することでお互いを参照するような構造になっています。どんな感じに依存しているかはmebiusboxさんの記事を見るとかなりわかりやすいです。

zenn.dev

GLTF material

標準のGLTFはオフラインレンダリングで使うには少々マテリアルや光源関係の情報が足りませんが、GLTFはextensionと呼ばれる拡張が考えられており、以下のextensionに対応しました

  • KHR_material_emissive_strength : Lightの強さの値(デフォルトだとRGBでしか管理してない)
  • KHR_material_sheen
  • KHR_material_transmission
  • KHR_material_ior
  • KHR_material_punctual : 点光源などを含められる(アニメーションあり)

以上が一応レンダラーに組み込んだextensionですが、ほかにも公式が決めているextensionがありやろうと思えば、オフラインレンダリングでもGLTFで事足りる気がします。

github.com

ただ問題として、このextensionが公式で決められたのは比較的最近なようで、意外とBlenderが一部extensionに対応していないことがありました。

BlenderのGLTF出力については公式ドキュメントによるとちゃんとマテリアルのextensionは対応しているとのことでしたが、当時最新LTS 3.2ではなぜか出力できないということがありました。

docs.blender.org

どうやらこれはそもそもextensionに対応することが結構最近決まったらしく、まだLTS実装されていないということであると思われます。

これに対して、バージョンを3.3 alphaにして行ってみるとちゃんとextensionを出すことができました。なので結構間が悪かったみたいですね。

Animation

GLTF

GLTFのアニメーションデータはanimationと呼ばれる場所にデータとnodeのインデックスが入れられています。基本的にAnimationはTransform(平行移動), Scale(拡大、縮小), Rotation(回転)の3種あり、それぞれ別々にAnimationとして存在しています。また、そのAnimationのデータ内ではkeyフレームの時間,その時のAnimationの値という形でキーフレームの情報が入っており、間のフレームでは補完を行うことを前提に作られています。

実装としてはTransform,scale,rotationそれぞれでkeyframeとその値を入れる配列を用意し、それをAnimationというクラスでまとめて1つのAnimationのデータを管理する形で行いました。

ちなみに、GLTFではnodeからAnimationのインデックスを取ることができず、Animationからnodeを指定する必要があります(理由はあるんでしょうけどちょっと面倒)。対策としてはnode分Animationクラスを用意し、インデックスによって指定できるように作りました。

また、個人的に突っかかったところとしてはRotationの値はクオータニオン表記であったことです。初めてクオータニオン使ったのでなかなかデバッグが大変でした。

情報としてはこの辺から調べました。

github.com

なお、スキニングといった剛体じゃないAnimationも可能なのですがちょっと手に余るので今回は実装していません。

また、Animationはそれぞれのnodeに対して設定されるため、以後の説明であるInstaceはnodeごとに作る必要があります。

Optix Instance Animation

Animationを行う場合、OptiXのASの再構築を行う必要があります。

OptiXのInstaceにはtransformという12個の要素を指定する配列があります。これはアフィン行列の上3行を示すもので対応するアフィン行列の値を入れてあげることでInstanceの平行移動、回転、拡大縮小というのがここで行うことができます。

Animationを行う際、各nodeのアニメーションからアフィン行列を取得→各Instanceのtransformを更新→ASを再構築という手順で毎フレーム行うことでAS上のアニメーションを作ることができます。

ただし、同時に変形に伴ってユーザーデータとして渡している各頂点情報についても処理する必要があります。NEEなどでは直接頂点情報を読み込みたいことが多いため、更にユーザーデータでもInstanceのtransformを渡して置き、CUDA側でもtransformを参照できるようにして置くと扱いやすいと思いますので私は各Instanceのtransform、そのポリゴンがどのInstanceに所属しているかを表すindexなどを追加し直接参照できるように実装しました。

今回は行いませんでしたが、skiningなどを考える場合はGASの頂点配列を更新していく形で(多分)実装できると思います。

NEE&MIS

いつも通りNEEとMISまでとりあえず実装しました。ただ今回は光源が多いシーンを考えており、少し光源サンプリングを変え、簡単な光源重点的サンプリングを実装しました。

単にこれは各光源のトライアングルについて面積×輝度というウェイトを用意し、これをpeacewise-function(IBLの実装参照)のサンプリングでサンプルするというだけです。実装としては毎フレームごと事前にすべての光源トライアングルのウェイトを計算、それを元にCDFの配列とポリゴンのインデックス配列を用意し、GPU側ではそれからサンプリングを行うという形です。

これをやると輝度が低いのに面積が広い光源&輝度が高いのに面積が小さい光源が同時に存在するようなシーンを効率的にレンダリングすることができます。結構光源が多いシーンだとこうしたものを考える必要があり、今回そうゆうシーンを作りたかったので実装しました。

光源一様サンプリング 極端に輝度と面積が異なるとノイズが出てくる

光源重点的サンプリング 先ほどと比べると青いやつのノイズはかなり減っている

また、MISではなかなかDisneyBRDFのバイアスが取れず、何度かDisneyBRDFの修正を行い、最終的にこの辺でマテリアル関係が完了しました。

ちなみにNEE側ではIBLの重点的サンプリングは実装しておらず、光源サンプリングの対象はポリゴン光源+DirectionalLightだけにしています。IBLの重点的サンプリングは中々GPUで実装するには面倒だし太陽光が入ってないIBLを使えば大抵何とかなるのでこんな感じになりました。

OptiX Denoiser

OptiX Denoiserは機械学習ベースのデノイザーであり、一般的な商用レンダラーでも使われている有名なデノイザーです。OptiX DenoiserはOptiX SDKに含まれており、OptiXで開発してればすぐに導入することができます。

OptiX Denoiserはレンダリング結果とその画像におけるAlbedo, Normalをデータとして渡すことでデノイズ画像を出力してくれます。レンダリングした後のポストエフェクト的な処理なので後から実装しても簡単に組み込むことができます。依存するところはCUDAstreamを渡すとこぐらいです。

Normalのデータは(0,0,0) ~ (1,1,1)に正規化する必要があるので事前に(Normal + 1.0) * 0.5という処理を行って渡す必要があります。

一方アルベドのデータについてはそのまま一番最初に当たった物体のalbedoを渡しても問題ないのですが、ガラスや鏡面などがある場合それらのalbedoではなく反射、屈折した先のalbedoを取るとよりそれらのデノイズ結果が綺麗になるそうです(特徴的な色を扱うべきらしい)。一応私の実装では、roughnessが低いようであれば反射屈折した先の色を取得するようにしました。

デノイズした結果は結構綺麗で静止画であれば十分リファレンスみたいになります。

デノイズの比較 Denoiseするとかなり綺麗になる

Temporal Denoise

OptiX Denoiserも銀の弾丸というわけではなくノイズが多い画像をデノイズするともやもやした感じのノイズが生じてしまいます。静止画であればよっぽどじゃないと目立ちませんが、一方でこれがアニメーションになると各フレームでもやが変化してしまい、レンダリング結果に気になるもやが現れてしまうということが起きてしまいます。

以下の動画だとなんか映ってるものが靄ついて見えますがこれは画質の問題とかじゃなくてDenoiseによる模様で、結構目立ってしまっているのがわかると思います。

これの対策としては単にサンプル数を増やして綺麗なデノイズ結果を出して減らす以外にも、Temporal Denoise(Filter)という処理を行うことで解消されます。

アニメーションの際、大体の場合前後のフレームで映る場所というのはほとんど変わらないことが多いです。例えば、カメラが単に右にほんの少し平行移動するだけであるような状況では現在のフレームの描画情報は前のフレームの描画情報を数ピクセルだけ移動させたような状況もあり得るわけです。これが意味することは、前のフレームが既に現在のフレームでも欲しい情報を既に持っているということです。

ということで前のフレームからある程度情報を持ってきて現在のフレームの描画情報と合わせることで、アニメーションを滑らかにフィルタリングしようというのがTemporal Denoiseというものらしいです。

OptiX Denoiserは一つの機能としてこのTemporal Denoiseを行うことができ、先ほどのデノイズ情報に加え「前フレームのデノイズ結果」、「Flow Vector」の情報が必要とのことらしいです。

「Flow Vector」というのは前のフレームのピクセルが次のフレームで移動したピクセルへの移動量を表すベクトルです。これを使えば今のこのピクセルは前のフレームのどのピクセルなのかを計算することができ、前のフレームの情報を扱えるというわけです。

カメラ不動で何も動かないのであればFlow vectorは全て0でよく、それをやった結果が以下の動画です。これだとかなりもや付きが抑えられており、床面やガラスなどがAnimationでもくっきり出ています。

しかしながら、中々このFlow vectorの計算が難しくなんか調べてもこれ一択!みたいな手法がありませんでした。というかほとんど情報がないです。どうやら一般的な用語としてはFlow vectorではなく Motion vectorらしくこちらを調べていく方がよさそうな気がします。

一応以下の資料にそれっぽいのがあったので実装してみたのですが中々うまくいかず、結局時間的に断念しました。

GDC Vault - Temporal Reprojection Anti-Aliasing in INSIDE

モデル制作

レンダラー側の実装が大まかに終わったので、最後の1週間はシーンづくりを行っていました。最近エルデンリングをやったのでなんかそれっぽいので、レンダラーの機能ちゃんと使えるような複雑なシーンにしていこうという方針でBlenderでなんやかんや作っていきました。

また、結構前に買ったSubstance painterをちゃんと使いたいな―と思っていたので練習がてら1から全てテクスチャを制作していきました。その際、Substance painterBlenderの連携がなく、テクスチャとかの反映が面倒くさかったのでなんかいいアドオンないかなーと調べたところ、live linkという連携アドオンを見つけました。ちょっと高めでしたがボタンクリックでモデル、テクスチャのお互いの送信ができ、かなり作業が効率化しました。

xolotlstudio.gumroad.com

話は変わりますが、8月だったかぐらいにキーワード送るとそれに沿った絵が出てくるAIが流行っていたと思います。その際、私はtwitterでそれを使って3Dモデルのテクスチャとして生成している人を見て、テクスチャ生成ソフトとして結構便利なのでは?という期待を持っていました。今回、せっかくなのでmidjourneyに課金してモデルのいくつかのテクスチャを生成できないかと試してみました。

最初は本の表紙の生成をやってみたところいい感じのものが出るのですが、背表紙を含めた形で出すことができずモデルに使うには難しい画像しか生成できませんでした。他にも模様などの生成をしてみたのですが、模様が細かすぎたり、余計なデザインが入っていたりと満足なものを出すことは難しかったです。

最終的には地球儀の古い世界地図はうまいこと言ったのでシーンに採用したのですが、他は中々雰囲気に合わない、必要なものがないなどそううまくは行かない感じになってしまいました。

地球儀にMidjourneyで生成したテクスチャを使用

ですが、なんかの設計図とかラベルとか元々紙に書かれるものはかなりいいものが作れ、模様とか妥協すれば壁紙などに使用できそうだなぁという所感があります。もう少しうまいことできるように調整が聞くようになったらかなりテクスチャ制作は飛躍的に効率化しそうだなという気がします。今後、PBRテクスチャを生成するAIとかが出てきたら3Dでもかなり大きな恩恵を受けれそうで期待があります(著作権周りどうにかして)。

制作したモデルは魔法の図書館的な感じで(ぶっちゃけレアルカリア)、かなりSpecularとDiffuseが入り混じるマテリアルでふよふよ浮く物体など大方のレンダラーの機能を使えるようなシーンにすることができました。

レンダリング結果

せっかく作ったので配布してみているので興味があればサンプルシーンなどにどうぞ。

motimoti3d.booth.pm

レンダラー提出当日

そうして提出2日ぐらい前にようやく全て終わり、ついにレンダラーを提出する段階に入りました。

レイトレ合宿ではAWSのEC2上のインスタンスでレンダラーを実行するという形で実際にレンダラーを動かされます。そのため、事前にAWSで自前で環境を作り実行できることを確認しておく必要がありました。

しかし、私はAWSを触ったこともないしEC2なんて初めて聞き、挙句には作ったプログラムを他の環境で動かすこと自体も下手したら初めてでした。慣れない手つきでAWSを使ってEC2インスタンスを立ててみたのですが、vCPUの申請というものを出して許可されない限りインスタンスを立てられないということを知らされ、期限が明日だってのにいつ来るんだかわからない許可を待つはめになりました。

とりあえずOptix使ってる人が大半だろうし多分動くだろうと提出してみたところ、期待は打ち砕かれ実行できないという結果を受けました。結局、shockerさんが代理で実行していただけることになり(本当にありがとうございました)、ご助力もあり実行できないバグを期限1時間ぐらい前に解消することができました。


私はよっしゃ~これでもう大丈夫だ~!と飛び上がって喜んでいました。出力結果見るまでは

出力したレンダリング画像を見てみました。それで現れたのはこれです。

なぜか全くモデルが表示されずIBLの星空しか映っていないですね。別のフレームでも見てみましょう

綺麗な星空ですよね。

全く予想だにしていなかったバグを目の当たりにし、11時半を過ぎた時計を見て私の気力はすべて吹き飛びました。

刻刻と流れる時計を見ながら、missシェーダーでも書けば出るような画像を出すレンダラーをとりあえず出しました。それで12時を回りました。

せっかく頑張ったのに~という悲しみと何が原因なんだという疑問を抱えたまま就寝することになりました。その頃の私はこんなんでした

ちなみにvCPUの許可は終わってすぐに来ましたゆるさん

このバグは恐らくSDK exampleをそのまま使ってしまっていたということが原因だと思われます。SDK exampleはちょこちょこ絶対パスを使っている節があり、先ほどの実行できないバグはptxファイルのパスが絶対パスであったことが原因でした。

多分まだ絶対パスによる指定が混じっており、Shader周りがちゃんと読み込まれていないことによるバグなんだろうなぁという気がします(手元では問題なかったため)。まさかclosest hitだけが実行されない or anyhitが誤作動を起こすとは思いませんでした。

もし、今後新たに参加する人は最初の方でAWSで実行確認などを行っておくといいと思います。

最終的に提出したレンダラーのリポジトリは公開していますので興味があればどうぞ(ただしスパゲッティコード

github.com

レイトレ合宿1日目

台風が迫る中でも飛行機は飛ぶことができ、無事レイトレ合宿開催されることとなりました。徹夜明けで人生で(多分)初めての飛行機に乗り、昼下がりぐらいに沖縄に到着。割と空はちょこちょこ晴れている感じでした。

いいVolume

途中トラブルがありつつも何とかレイトレ合宿の皆さんと合流。その後、ホテルを見学しました。ホテルはかなり面白いところで皆さん口をそろえて「サンプルシーンみたい」と言っていました(Specularが強いシーンなので写真はなし)。

その後はさっそくセミナーが開催されました。スライドを発表している方の範囲ですと、ykozwさんが黄金比についての解説をしてくださいました。何で現実で黄金比が現れるのか、黄金比にまつわる数学の話などかなり面白い内容でした。


speakerdeck.com


その後は、yumcyawizさんがVolume Renderingについて話してくださいました。かなりスライドがこれ以上ないほどわかりやすくまとめられており、とても良かったです。


speakerdeck.com


また、たたさんがRustとGPUの話をしてくださいました。最近よくRustはいいぞという話は聞いており、VulkanのRustラッパーなどかなり貴重な情報を聞くことができました。


speakerdeck.com


他にも面白いセミナーが発表されており、とても興味深い話ばかりでした。

レイトレ合宿2日目

レイトレ合宿2日目はさっそく朝からセミナーが始まり、こちらも非常に勉強になるものでした。特に最近話題のNeRFやReSTIRといった興味のあるトピックについて解説があり、どうゆうものなのかなんとなく理解できてとても楽しかったです。NeRFを解説していたushioさんがスライドを発表していたので是非ご覧ください。

github.com


その後、会場に移動しデノイズ部門選手権が始まりました。この選手権は各自自作したデノイザーで同じ画像をデノイズし、どれが一番きれいだったか投票形式で競い合う大会です。



参加者は6名、各々のデノイザーは特色があり、デノイズ結果がそれぞれ異なっていてとても面白かったです。決勝戦もかなり表が分かれる接戦で見事1位を獲得したのはholeさんでした。holeさんのデノイザーは全体的に安定していて、毛玉や怪獣のような難しそうなシーンでもきれいにデノイズしているのがすごかったです。

その後はデノイザーの簡単な説明をして頂きました。意外と機械学習は少なく、半分ぐらいはルールベースでやっていたのは面白かったです。

そして、ついに本選が始まりました。



本戦ではレンダリングした動画を5回分流し、その後レンダラー解説という形で順番に各参加者の作品を見ていきます。今回の参加者は私含め16名、各々のレンダラーとレンダリング結果はそれぞれ特色があり、技術的にも映像的にもすごいものばかりでした。映像はレイトレ合宿のページで見ることができるので是非ご覧ください。個人的にはholeさん、shinjiさん、gam0022さんの作品がお気に入りです。


sites.google.com


ちなみに私はというと、皆さんの前に星空の映像が5回流すということをしました。3回目ぐらいなったら皆さんがちょっとどよついてたのが面白かったです。本当だったらこうなってたmovieも流して頂いて、一応皆さんに成果は見せれたので満足でした。

本戦はその後、朝までに各映像に1~10の点数を付けて、明日に総合点で順位がつけられます。

その後は予定を変え、ちょっとだけセミナーが行われました。スライドを上げている方だとgamさんがレイマーチングについて話してくれました。知りたかった手法が紹介されていたのでとても良かったです。

docs.google.com

食事後、夜のセミナーが始まりしょっぱな私の番が来ました。特に他に話すこともなかったので以前アドベントカレンダーで発表したMNEEやSMSについてを題目に色々話させて頂きました。強い人たちの前で説明するのは中々怖かったです。


speakerdeck.com


他の方も興味深い内容のものがあり、楽しんでセミナーを受けられました。スライドを共有している範囲だと、isiyamaさんがかなり物理学的に光について話してくださいました。一応自分は物理の身なので興味深かったです。

https://k-ishiyama.github.io/posts/202209_rtcamp8/rtcamp8_lt.pdf

レイトレ合宿最終日

最終日ではついに順位発表が始まりました。私は案の定16位中の16位という中々に悔しい結果になってしまいました。



ちなみに景品がそれぞれの順位に配られ、私は(なんか6袋ぐらい)レンズ豆を大量に頂きました。よくカメラのレンズはこのレンズ豆に似てるから「レンズ」という名前が付いたとは聞いてたのですが、実際に初めてみて「あ~ほんとにレンズの形だわ」と改めて分かったのでとてもうれしかったです。

景品のレンズ豆 いまだにどうやって食べるのかわからない

そして、栄えある本戦1位に輝いたのは納得のshockerさんでした。60fps 8秒という1フレームの時間がかなり限られる条件の中、様々な手法で高負荷な雲をレンダリングするという技術的、映像的にも素晴らしい作品でした。



順位発表が終わり、無事にレイトレ合宿は閉会しました。その後はkugiさんの景品だったプリズムで遊んでたり、観光したりとして無事に帰ることができました。



おわり

噂に聞いていたレイトレ合宿に参加することができてとてもうれしい限りでした。参加される方は皆さますごい人ばかりで、セミナーでは知らない情報ばかりでかなり刺激的でした。そういった方とお話できたことはとてもうれしかったです。

本戦の方は中々悔しい結果に終わってしまいましたが、今回のレンダラー開発では色々挑戦が多く今まで作った中ではかなり実用的なものに仕上げることができたのはとても良かったです。次回はちゃんと提出できるようにするぞ~

運営の皆様、とても面白い合宿でした!運営お疲れさまでした&ありがとうございました!

おまけの写真

沖縄的な写真

琉球ガラスのコースティクス