1. 今週の予定
去年と同じ内容をやっていきます。
<映像作品としてのLandscapeの作成>
建築用のLevelの作成の勉強の続きをやります。
建物の作成の続きをやります。
<Niagara の勉強>
「Unreal Engine 5で学ぶビジュアルエフェクト実装 基本機能からNiagara、シミュレーションまで」の実装をやります。
<Materialの勉強>
Ben Cloward先生のTutorialを勉強します。
<Gaeaの勉強>
GaeaのTutorialを作成します。
<Houdiniの勉強>
Castle Wall Tool [1]の勉強をやります。
<UEFNの勉強>
Pi Equals Three氏のTutorialをやります。
<DirectX12の勉強>
「DirectX 12の魔導書」の勉強をやります。
更にLötwig Fusel氏のDirectX12のTutorialをやります。
2. Landscapeの作成
2.1 Unreal Engine 5 for Architecture - 2023 Full Beginner Course [2]の勉強の続きをやる
先週は二個目のカメラを適切な位置にセットした後で、間違って移動させてしまい、直す前に3D酔いになってしまい、途中で終了したんでした。
まずCameraの位置を直しました。
Cameraから見た風景です。
地面の色が黒過ぎます。
直します。
以下のようになりました。
もっと草が生えてたりとか岩が埋まってたりとかしないとオカシイですね。
草とか岩を追加しますか。
まずAssetを確認します。
この辺のAssetには使えそうな草や岩が結構ありそうです。
今週はこれらのAssetの中身を確認する事にします。
と思ったんですがDownloadの速度規制が掛かっているみたいで、全然Download出来ません。
これは来週までにDownloadしておいて来週やる事にします。
<Procedural Biomes>
<Procedural Nature Pack Vol.1>
<Realistic Forest Pack>
<Nature Package>
<Scanned Poplar and Aspen Forest>
<Spring Landscape>
<Stone Pine Forest>
<Tropical Jungle Pack>
せっかく久しぶりにBridgeを開いたので以下のAssetをDownloadしました。
< Unreal Engine 5 for Architecture - 2023 Full Beginner Course [2]の続きを勉強する>
2023-10-22のBlogには以下のようにまとめられていました。
のでCamera Setupの3つ目のCameraの部分から以下にまとめます。
<<Camera Setup>>
配置されているCameraをDuplicateして家の中に配置しました。
ここまでは今までのCameraの配置と同じです。
Cameraからの映像です。
Current Focal Lengthの値を50に変更しました。
うーん。
こっちの方が人間の視覚に近いみたいですね。
更に
Current Focal Lengthの値を35.0に変更しました。
うーん。
これは行き過ぎな気がします。
画面が暗すぎるのでPost ProcessのLensにあるExposureのExposure Compensationの値を変更します。
結果です。
おお、明るくなるだけじゃなくて、画面に芸術性が追加されましたね。
まだ画面が狭いと言って、
Current Focal Lengthの値を更に下げました。
結果です。
うーん。
画面中央にある椅子やSofaが細く見える気がします。
このCurrent Focal Lengthの値の設定をこの値にする理由はよく分からないです。
Sphereを部屋の中に配置しました。
Sphereを配置すると画面がまだ暗いのが分かります。
Exposure Compensationの値を11.5に上げました。
結果です。
更にCameraをDuplicateして以下の画面を撮影しています。
画面が暗いのでExposure Compensationの値を16.0に変更しました。
結果です。
芸術的な画面かもしれませんが、Photo-Realistic感はなくなりましたね。
Current Focal Lengthを18.0にセットしました。
結果です。
Focus Distanceを
以下のLamp?に合わせました。
これでCamera Setupは終わりでした。
この画面のPhoto-Realistic感が無いのは次の節であるLight Sourcesで直すそうです。
< Unreal Engine 5 for Architecture - 2023 Full Beginner Course [2]のCamera Settingを実装する>
室内のCameraの設定方法は大体理解したので実装します。
以下にCameraの設定方法をまとめます。
- 画面が狭かったらCurrent Focal Lengthの値を下げる
- 画面が暗かったらExposure Compensationの値を上げる
- Spheresを画面内に配置して画面の暗さなどを確認
- もう一回、Current Focal Lengthの値を調整
- もう一回、Exposure Compensationの値を上げる
- Focus Distanceを調整
こんな感じです。
それではやってみます。
一個目の室内Cameraからの画面です。
Current Focal Lengthの値を15.0にしました。
結果です。
おお、ぐっと情報量が増えました。
Exposure Compensationの値も変更してみます。
8.21だったのを10.0に変更しました。
結果です。
おお、少し明るすぎるかもしれませんね。
後、これだけ日光で明るいなら窓枠の影が床に写ってないと変です。
が
これはどうやって直せば良いのか分かりません。
これからの課題とします。
最後にFocus Distanceを以下の位置に変更しました。
結果です。
うーん。
良い感じです。
Spheresをここに移動させて更に細かい調整をします。
Sphereを配置しました。
うーん。
今のままでも良い気がします。
そもそもこのSpheresをどうやって活用すべきなのか知りませんでした。
黒い球は十分に黒く見えますし白い球はそれなりに白く見えます。
それそれの球のShaderもはっきり確認出来て球の凹凸ははっきり認識出来ます。
今週は3D Gaussian Splattingも試すのでUnreal Engine 5 for Architecture - 2023 Full Beginner Course [2]の勉強はここまでとします。
2.2 Step-by-Step Unreal Engine 5 Tutorial: 3D Gaussian Splatting for Beginners [3]を実装する
まず先週のBlogをしっかり読み直します。
読み直しました。
うーん。
やってみますか。
XV3DGS-v1.10-ue5.3をDownloadします。
しました。
解凍しました。
UE_5.3のPluginsの中に配置しました。
そしてUE5.3を起動させます。
起動させたらXV3dGSをEnableし
UE5Editorを再起動します。
再起動したらTool BarのPlatformsの脇に新しくXのIconが表示されるようになりました。
先週見た
とはIconの形状は違っていますが機能は多分同じでしょう。
Cursorを乗せると以下のような説明が現れました。
Clickしたら
以下のBoxが表示されました。
ここに3D Gaussian SplattingのDataを入れるのか。
うーん。
PolycamのHomepageに来ました。
Splats->と書かれている箇所がありました。
これをClickしてみます。
ここにあるDataをDownloadする事が出来そうですね。
Tutorialを見ると
でDownload出来るDataを表示しています。
うーん。
これは分からない。
とりあえずDownloadしてみます。
Accountを作成しろと出て来ました。
はい。
名前なんかを登録したら以下の画面が出て来ました。
Albumを作成しろ。とありますがこれは必須なんでしょうか?
なんか課金されそうで嫌なんですが。
Albumを作成してDownload Model after SavingにCheckを入れて保存を押しました。
以下の画面が表示されました。
Export toにUnrealを選択してDownloadを押しました。
ずっとグルグルまわっているので一端中止します。
もっとSizeの小さいのをDownloadする事にします。
これをDownloadしましょう。
DataをExportするにはProのlicenseを得る必要があるみたいですね。
うーん。
GAUSSIAN SPLATを選択したらDOWNLOAD Buttonが押せるようになりました。
押したらまたグルグルが始まって何も起きません。
PolycamのDataのDownload方法を調べます。
Exportはお金を払わないと出来ないとありました。
うーん。
今週はここまで。
流石に課金してまで試す気にはなりません。
2.3 現実の映像とUEを組み合わせる方法を勉強する(追記)
3D Gaussian Splattingは実際に撮影した映像を元に3D Graphicsを作成します。
これとは別に実際に撮影した映像をそのまま3Dに組み込んでCGを作成する方法があります。
これについて少し調査します。
以下の二種類があります。
- 背景を3Dで作成してGreen Screenで撮影した人物をその3Dに組み込む
- 背景+人物も2Dで撮影したものにVFXとして3Dを追加する
それぞれについて調査します。
<背景を3Dで作成してGreen Screenで撮影した人物をその3Dに組み込む>
以下のTutorialが見つかりました。
これ見るとCinecom.net氏が沢山それ関連の動画を作成しているようです。
それに対して
<背景+人物も2Dで撮影したものにVFXとして3Dを追加する>
これはUEで出来るのかどうか分かりません。
しかしBlenderでは出来るのは以下のBlenderのTutorialで分かっています。
これと同じ事、もしくはこれと近い事をUEで出来るのかを調査します。
このTutorial内で動画を撮影したCameraの位置を計算している場面があるのですが、UE単独ではそれをやる機能はないみたいです。
なのでAfter Effectを活用してその効果を得る方法を取るみたいです。
多分ですが以下のTutorialでその方法について説明しているかもしれません。
一寸確認します。
<After Effects to Unreal Engine 5 (THE EASY WAY) [8]を軽く見る>
やっぱりAfter Effectで3D CameraのMotion Trackerを使用していました。
最終的にUE内で以下の映像を作成していました。
これはBlenderで見たのと同じです。
After Effectを使用すれば2Dの映像とUEの3Dを組み合わせる事が可能で有る事が分かりました。
でBlenderで作成したMotion TrackのDataをUE5にExportしたらAfter Effectでやっている事と同じ事が出来るじゃね。と
それについて調べました。
TRACK THIS! - Camera Tracking From Blender To Unreal Engine [9]で近い事をやっていました。
この動画もJoshua M Kerr氏が作成していました。
この人の動画は要Checkする必要がありそうです。
こんな感じでした。
3. Niagara の勉強
「Unreal Engine 5で学ぶビジュアルエフェクト実装 基本機能からNiagara、シミュレーションまで」の続きを勉強します。
3.1 先週の復習をする
四章の「木の生成Simulations」の最初の3つの節を勉強していました。
「14.1 木の生成Simulationsの理論」では
まず木の生成のAlgorithmをまとめています。
ただしこれを実際に実装するためには、枝(幹)の始点と終点を管理してその間に線を引く必要があります。
それについてその後で具体的なやり方をまとめていました。
「14.2 Niagaraで線を書く」では
Niagaraを使用して2つのParticleの間の線を書く方法を解説していました。
「14.3 木の生成Simulationの実装」では実際の実装方法について解説していました。
3.2 「木の生成Simulations」の実装をやる
「14.3 木の生成Simulationの実装」で勉強した部分を実装します。
実装する前にEmitterのTemplateをどれにするのが良いのかを決定する必要があります。
Sample Codeを見て検証します。
以下のEmitterです。
うーん。
これだったらEmptyでも良い気がしますね。
とりあえずEmpty Emitterで実装する事にします。
Preview画面で完成した木の画像を見る事も出来ました。
こんな感じで枝が生えている木って確かに存在してますね。
まずNSを作成しました。
名前はNS_FractionalTreeとします。
Tutorialでの名前とは違いますがNiagara SystemはNSで統一したいのでこれで行きます。
Empty Emitterの名前を変更します。
こっちはTutorialと同じFractionalTreeにしました。
こっちはTutorialと同じ名前です。
EmitterのProperties Moduleを選択してSim TargetをGPUに変更しました。
更にいつもの設定の変更をしました。
更にEmitter Update SectionにSpawn Burst Instantaneous Moduleを追加し
Spawn Countに700をセットする事で
Particleを700生成します。
Tutorialの説明ではこれが枝の始点と終点になるとありました。
次にEmitter Update SectionにあるEmitter State System Moduleの設定を変更します。
以下に示した様に変更しました。
これも発生したParticleをずっと表示するための定型的な設定ですね。
CGHOW氏のTutorialで散々やりました。
次にParticle Spawn SectionにあるInitialize Particle ModuleとParticle Update SectionにあるParticle State Moduleを消します。
教科書ではこの2つのModuleを消す理由をParticleの消滅をしないからと書いていますが、これってどうなんでしょう?
確かにInitialize Particle Moduleでは以下のようにLifetimeを指定していますし、
Particle State Moduleでは以下のように
実際にParticleをKillしています。
でもInitialize Particle Moduleに限って言うと、このModuleの機能って作成したParticleのLifetimeを指定するだけじゃないです。
今回はLifetimeを指定する必要が無いうえにParticleを消滅しないだけでなくParticleそのものの色やTransformなどのAttributeを指定する必要がないからInitialize Particle Moduleを消しても問題ない。というのがより正確な気がします。
消しました。
次に変数を作成します。
教科書の説明では[Particle] Positionは最初から存在しているのでそれをParticleの始点として利用すると言っています。
しかし私のNSのParameterを見ると
Particle Attributeに一個のParameterも表示されていません。
一応、Render SectionにあるSprite Renderer Moduleの
Bindingsを見るとPosition Bindingで[Particle] Positionを使用しているので
ある事はあるみたいです。
この状態でParticle Attributesに[Particle] Positionを追加します。
なんと名前が変更出来ません。
これはParticle Spawn SectionにあるInitialize Particle Moduleを消したからじゃないでしょうか?
復活させてみます。
Particle Attributesに以下のParameterが表示されるようになりました。
よしこれから新しいParameterを追加出来ると思ったら
またErrorになりました。
なんと[Emitter]側に強制的に移動されて作成されました。
うーん。
何となく理由が分かって来ました。
私は新しいPosition型のParameterを作成している気になっていたんですが、ここに表示されたParameterは[Particle]PositionというもとからあるParameterの様です。
Make NewからPositionを選択しても既にある[Particle]Positionに飛んでしまいます。
何で?
何回か試していたら突然、新しいPosition型のParameterを作成出来ました。
名前をEndPositionに変更しました。
うーん。
ここは深堀すべきところなのか?
そのまま無視して進んでも問題ない気もします。
うーん。
先に進みます。
今度はInt32型のDepthという変数を作成します。
これはそのParticleが最初のParticleから数えてどの深さなのかを記録するためのVariableだそうです。
こっちは何の問題もなく普通に作成出来ました。
次にVector型の変数を作成します。
Forwardと言う名前だそうです。
これも普通に出来ました。
次にAttribute Reader型の変数をEmitterに作成します。
Particle Attribute Reader型しかありません。
これで作成しました。
これで正しいのかSample Codeで確認します。
Particle Attribute Readerと表示されています。
以下のように表示されています。
あってるでしょう。
名前だけAttribute Readerに変更しました。
今度はGPU Simulation Stageを追加します。
名前はSolveとしました。
Solve Sectionに新しいScratch Pad Moduleを追加します。
名前はこれもSolveとします。
Map Getノードに以下のParameterを追加しました。
Custom HLSLノードを追加しました。
Map Getノードにある全てのInputをCustom HLSLノードにPassしました。
Custom HLSLノードのOutputを作成します。
次はMap setノードにParameterを追加します。
しました。
Custom HLSLノードのOutputとMap SetノードのInputを繋ぎました。
ここでApplyを押して一端、Scratch Pad Moduleの作成を終了します。
Solve SectionにあるSolve Moduleを選択し
Attribute Readerに[EMITTER] Attribute Readerをセットしました。
ここからCustom HLSLノードの実装になります。
これは来週やる事にします。
4. Materialの勉強
先週勉強した Using Custom HLSL Code - Advanced Materials - Episode 24 [4]を実装します。
4.1 Using Custom HLSL Code - Advanced Materials - Episode 24 [4]を実装する
こっちもCustom HLSLノードの使い方です。
以下のようなCodeを書きました。
Float3の値を色々変更しました。
Descriptionの値を変更しました。
結果です。
Customノードの名前が変更しました。
次はInputsの使用方法についてです。
以下のような実装をしました。
Input Nameを追加して名前をColorにしました。
CustomノードのInputにColorが作成されました。
これだけだとInputした値が結果に全く反映されないので
Codeを以下のように書き換えました。
更にInput先にFloat3型の変数を繋げました。
Inputに関しては2つ問題があります。
それぞれ検証します。
まずCustomノードのInputに何も接続しないとErrorになる事です。
うーん。
Default値を設定出来ると思ったんですが、そんな設定はどこにもなかったです。
次の問題はInputに繋げられるNodeのTypeを指定する箇所が無い事です。
Float1のNodeを代わりに繋げてみます。
別にErrorにならないですね。
ただしCodeは以下のように変更しています。
<Lerp(A, B, T)>
今度はLerp()関数の使用方法についてです。
以下のように設定を変更しました。
更にInputも繋げました。
む、
Errorになっています。
Lerpじゃなくてlerpじゃない?
と書かれています。
直します。
以下のように直しました。
結果です。
今度は上手くいきました。
<Texture2DSample(Tex, TexSampler, UV)>
今度はTexture2DSample()関数の使用方法について実装して確認します。
以下のように設定を変更しました。
このTexSamplerが何を指定しているのかが不明です。
この件は先週散々調査したけど不明でした。
Inputに以下のNodeを接続しました。
結果です。
<For Loop>
今度はFor Loopを実際に使用してみます。
Material内ではLoop()関数を表すNodeは無かったはずです。
このCustomノードの使用方法はかなり利口です。ただしCostは凄い掛かるとは思いますが。
結果です。
うん出来てますね。
Materialを組むときにLoopが必要になったらこの方法でやる事にします。
<もう少し複雑なLoop>
今度は以下の実装をCustomノードに追加します。
まず実装部に以下のCodeを書き込みました。
うーん。
多分あってる。
流石にEditorの自動でTypoを指定してくれる機能なしで、しかもこんなに細かいSpaceにCodeを書くのは苦痛です。
更にInputに以下の値を追加しました。
そしてInputに以下の実装を追加しました。
Errorが出ています。
うーん。
Texture2DSample()関数のTextureの最初のTとSampleのSは大文字でした。
直します。
結果です。
うーん。
Textureを水玉模様に変更します。
結果です。
以下の値を変更してみます。
結果です。
おお、四角くなっています。
出来てますね。
<四角くならないための実装>
四角くならないための実装も試してみます。
そう言えば先週このCodeを読んで理解する事はやってなかったです。
まずCodeを読んで内容を理解します。
まずVariableがTex、UV、Directions、Quality、Size、そしてtexResがあります。
それぞれのTypeは
TexはTexture Object。UVはTexCoord[0]、Directions、Quality、SizeはFloat、そしてtexResはFloat2でした。
そしてPi2はFloatでPi×2=6.28です。
RadiusはFloat2でSize/texRes.xyです。
教科書ではSizeの値は8、TexRes.xyはそれぞれ512でした。のでRadiusは8/512 = 0.015625でした。
Radiusは半径と言う意味ですが何の半径なんでしょう?
あ、水玉の半径だ。
水玉の半径が8 PixelsだとするとTextureのSizeが512になるのでTextureに対する割合は8/512になります。
Directionsが何なのかが分からないですね。
Floatで値は16になっています。
方向を示すならVectorでFloat2である必要があります。
うーん。
分かりました。
16分割しているんです。
16の方向にずらして値を取っています。
次のQualityですが以下のFor Loopでiの値を出すために以下の実装で使用されています。
あ、分かりました。これは16方向にどれだけ伸ばすのかを決定しています。
それぞれのPixelから16方向に周りのSampleをとって更にQualityの数だけその方向を少しだけ拡張してSampleしている訳です。
もう理解しました。
後、Customノード内のSin(x)のxはRadian表示のようですね。2piで360度と同じになっていますから。
UEのSin(x)はDefaultでは1が360度と同じ値になるように調整されているので、一応確認のために書いておきます。
もう全部理解したので実装します。
以下のように打ちました。
合ってるはずです。
それぞれのParameterです。
結果です。
うーん。
ぼやけ過ぎて何だか全く分かりません。
Sizeの値を小さくしたら
以下のようにBlurなImageになりました。
うん。
できています。
5. Gaeaの勉強
5.1 先週作成したPower Pointで動画を作成する
音ズレが激しくて何回も直しました。
一応、これで完成とします。
前回の動画を確認したら5回しか再生されていません。
Tutorialの最初の動画は80回位再生されていますが、回を重ねる毎に再生回数が落ちています。
まあ、再生回数は気にしないで兎に角、Tutorialを完成させる事を第一に考えてやっていきます。
5.2 ErosionノードとSedimentノード
Sedimentノードが以外と使える事が判明しました。
のでそれをまとめておきます。
以下のような地形があるとします。
Sedimentノードを追加します。
Depositが追加されました。
ただし全体に均一に追加されるのではなく傾斜が激しい場所や高度が周りに比べて高い場所にはDepositは追加されていません。
その結果、Terrainの特長、例えばこの場合ならStackの特長が、しっかり確認出来ます。
これがAlluviumノードやDepositノードだと以下のように全体に均一にDepositが追加されTerrainの特長が完全に消えてしまいます。
更にDepositsの場合だと線状の盛り上がりが形成されてしまいます。
Erosionノードを追加した場合は以下のようになります。
Depositsは適度に追加されています。
がSedimentノードを追加した時のようなそれまでの地形の特長は消えてしまっています。
Erosionノードの結果をよく見るとDepositsの追加だけでなく水が流れた後のような溝が形成されています。
この溝の形成がそれまでの地形の特長が消えてしまう原因のようです。
同様の現象はStratifyノードで形成された地形でも起きています。
Sedimentノードを追加した結果です。
Stratifyノードの特長がしっかり確認出来ます。それでいてDepositが追加されているのも確認出来ます。
Erosionノードを追加した結果です。
Depositと溝が形成されています。
ただし、この場合は前の地形の特長が少しだけ残っていました。
前の地形の場合もErosionノードのParameterを変更したら元の地形が確認出来るかどうかの確認をします。
結果です。
うーん。
これだとかなり前の地形の特長が確認出来ますね。
やっぱりErosionノードが一番使いやすいですね。
5.3 これからのTutorialの予定
一応今回で以下の3つのNodeを組み合わせる事で、
以下に示した3つの工程の
- 地形(Terrain)を作成
- 色を付ける
- Buildをする
の一番最初の工程である「地形(Terrain)を作成」が終わりました。
ので次の工程である色を付けるを始めようと思います。
5.4 色の追加の仕方を検討する
ここで別のGraphを利用する方法も教えてしまいましょう。
まず一色で塗る場合の実装を示します。
Erosionノードの終わりでTerrainの作成は終わりです。
その後で、Fxノードを追加しています。
Fxノードを追加したのは、これはここでTerrainの作成が終わっているという印になります。
そしてBuildする時にこのFXの結果をExportする事になります。
次に色を追加します。
まず単色の色を追加します。
これはPrimitives GroupにあるConstantノードを追加する事で実行されます。
ConstantのPropertiesにあるOutputの値をColorに変更します。
そして色を変更します。
Defaultでは黒に設定してあるので赤くしました。
結果です。
赤一色でTerrainが塗られました。
右上にあるTool Barの+を押します。
新しいGraphが形成されました。
以下のようにFXからLineを引っ張って
Mouseの左Clickを離すと以下のBoxが表示されます。
そのBoxにはMake Portalと言う選択肢があります。
これを選択します。
こんな感じになります。
次にColorのGraphに移動します。
そこに先程のConstantのノードを追加しました。
InputからLineを引っ張ってBoxを表示します。
Portalsを選択します。
するとFxと言う名前のPortalが表示されるので
それを選択します。
これで前のGraphのFxの結果がConstantノードに繋げられました。
当然このConstantノードのPropertiesの設定は前の解説と同じように変更しています。
結果です。
赤一色ですがTerrainに色を塗る事に成功しました。
6. Houdiniの勉強
6.1 Houdini - Wall Tool 05 [5]を勉強する
先週、Houdini 19 - Wall Tool 04 [6]の実装が終わったので今週はその次のTutorialを勉強します。
以下にその内容をまとめます。
今回のTutorialは目次がありません。
どうして無いんでしょうか?
Descriptionを見たら以下のKey Momentsがありました。
これって目次じゃないの?
うーん。
これに基づいてまとめます。
<Intro> 0:00
今回のTutorialでは先週加えたそれぞれのBrickのSizeのばらつきに更に少しだけNoiseを追加するそうです。
そのためにFor Each Loopの特性を利用するそうです。
<Meta Import> 0:39
そのためにMeta Import Nodeを使用するそうです。
まずForeach Beginノードを選択します。
以下のBoxがParameter Paneに表示されました。
<Create Meta Import Node> 0:45
Create Meta Import NodeをClickします。
以下に示した様に
Foreach Begin1 Metadata1ノードが作成されました。
このNodeには
ParameterにDetail Attributesがあります。
これを使って何かするみたいです。
ここで今回何をするのかが分かりました。
それぞれのBrickの位置を少しだけ凹ましたり、飛び出したりするそうです。
Attribwrangleノードを追加しました。
そしてforeach_Beginノードの結果を第一のInput Pinに繋げました。
更にforeach_Begin_Metadataノードの結果を第二のInput Pinに繋げました。
こんな結果になりました。
ここからそれぞれのPointのNormal Vectorを表示して
これらを利用してBrickを凸凹にします。と説明しています。
そしてそのためには少しだけVectorの数学を利用します。とも言いました。
以下の図で説明しています。
赤で囲んだ点がPointで青の矢印がそのPointのNormalです。
このNormal Vectorに対してUp Vectorを追加すると以下のようになります。
Up Vectorってなんでしょうか?
聞いたことないですが。
まあNormal Vectorに対して垂直なVectorを加えただけでしょう。
この2つのCross Productsを計算します。
すると以下の図で示した茶色のVectorのようなこの2つのVectorに対して垂直なVectorが現れます。
うーん。
成程。
このVectorにRandomな値をかけてその値をPointの値に足したらそれぞれのBrickが少しだけ凹んだり飛び出したりするようになります。
うーん。
これは勉強になった。
これをCodeに落とし込みます。
<Construct an Up Vector> 4:54
Up VectorはY軸に沿ったVectorなので{0,1,0}で表しています。
HoudiniはY軸が上を指しています。ここは注意が必要です。
今度はこのv@sideを可視化します。
以下のIconをClickします。
<Visualizers> 5:44
すると以下のBoxが表示されます。
Sceneの+をClickして以下のBoxを表示させます。
Markerを選択します。
以下のBoxが表示されます。
Labelの値をSideに変更します。
更にAttributeの値もsideに変更します。
そしてStyleの値をVector Trailに変更しました。
最後にLength Scaleの値を少しだけ下げました。
結果です。
黄色い線が追加されました。
今度はRandom Valueを作成します。
この辺は想像通りでした。
Rand()関数を使用してRandom Valueを作成するCodeを追加します。
最初のParameterはSeedを指定しますが、ここで一寸注意する必要があるそうです。
Primitive Numberを使用するとFor each Loop内では常に0になってしまうからだそうです。
そうなのか?
この辺は初めて聞く話ばっかりでそう説明されても???となってしまいます。
もっと勉強続けたら成程。と思えるようになるんでしょうか?
で、ここで先程作成したForeach Begin1 Metadata1ノードが活躍します。
Foreach Begin1 Metadata1ノードのAttributeにあるIterationを使用するそうです。
え、そんなのあったの?と思って確認したら
以下に示した様に
Detail Attributesの後ろにIterationで書いてありました。
うーん。
これってそういう風に見るものだったのか。
以下の実装でForeach Begin1 Metadata1ノードのAttributeにあるIterationの値を取り出します。
まずDetail()関数を使用します。
そして最初のParameterである1は
以下に示した様に
Foreach Begin1 Metadata1ノードの結果が、Attribwrangle2のInputの2番目に接続している事を示しているそうです。
これは0から数えているから当然ですね。
次のParameterである”iteration”はどのAttributeの値を取り出すのかを指定しています。
そして最後の0はIndexを指定するためのもので0で問題ないそうです。
これは初めて勉強する関数ですね。
一応、公式Siteのdetail expression function [7]の情報も確認しておきます。
あんまり詳しい解説ではないですね。
それぞれのParameterに関しての説明もほとんどないです。
最初のParameterの名称がSurface_Nodeになっていて
ExampleではNode名を指定しています。
あれ?
これTutorialの解説と違くない?
もう一回Tutorialの解説を確認します。
やっぱりここでまとめた説明をしています。
どっちの指定方法でも良いんでしょうね。
適当な数をかけてRand()関数を閉じます。
最後に0.5を引きます。
これはRandomな値の範囲を-0.5~0.5に変更するためです。
最後括弧で全部の式を閉じます。
この時この括弧の事をBracketと呼んでいました。私は括弧の事をParenthesesとしか読んだこと無いです。
と言う呼び方が一般的だと思うんですが、Canadaでは違うんでしょうか?
イギリスは違う呼び方をするって話は前に聞いたことがあるので、もしかしたらCanadaでも違うのかもしれません。
最後にMultiと言うAttributeの値を掛けました。
ナニコレ?
と思ったんですが、これParameterで壁をどのくらい凸凹にするかを調整するためのものですね。
このParameterを生成するために以下のIconをClickします。
このIconの機能については先週勉強したので覚えています。
作成したAttributeの値を指定するためのBoxを生成するんです。
Tutorialの動画を注意深く見ると、
以下のBoxが生成されていました。
これでRandom Valueが生成されたので、次の工程は、この生成されたRandom Valueに基づいてPointの位置を移動する事です。
それはどうやるんだ。
と思ったら以下の実装で簡単に済ましていました。
本当に+=って便利な機能ですね。
Attribwrangleノードの名前を変更し、Attribwrangle _brickJitterとしています。
更にParameterがあるNodeなので色を黄色に変更しました。
そしてその結果をCopytoPoints2ノードの2番目のInputに繋げました。
これでそれぞれのBrickの位置を凸凹にするための値がForeach Loop内にPassされました。
Foreach_end1ノードを選択し可視化します。
結果です。
Vectorを全部消します。
あれ、何も変化してないですね。
Attribwrangle_brickJitterノードに戻り、Multiの値を0から変更します。
結果です。
おお、Brickの位置が前後するようになりました。
これは大げさすぎるので以下の値に変更しました。
結果です。
おお、古い城の壁の感じが出て来ました。
今度はBrick同士の間にすこしだけGapを追加するそうです。
<Carve Node> 10:17
Carveノードを追加しました。
更にAttribwranble_brickJitterノードの結果をCarveノードのInputに繋ぎました。
Carveノードの結果を可視化すると以下のようになりました。
CarveノードのFirstUの値を変化させると
そしてRelative Channel Referenceを選択します。
すると以下の結果になります。
最初の値のRelative Channel ReferenceじゃなくてInverseな値が必要なので以下のように値を変更します。
結果です。
両方のPointが向きが反対ですが同じ量だけ移動するようになりました。
Carve_TweakBrickWidthノードと名前を変更しました。
更にParameterがあるNodeなので色を黄色に変更しました。
Carve_TweakBrickWidthノードの結果をCopytopoint2ノードの2番目のInput Pinに接続しました。
Foreach_end1ノードを選択し可視化します。
Brick同士の間に隙間が発生しました。
次にPoly Extrude 1ノードの設定を少しだけ変更するそうです。
Distanceの値に0.96を掛けました。
結果です。
上下のBrick同士の間にも少しだけ隙間が発生するようになりました。
ただこの隙間の幅は全部同じです。
これで終わりでした。
実装は来週やります。
7. UEFNの勉強
今週は先週勉強したPi Equals Three氏のMake a Book / UI Text In Fortnite - UEFN / Creative 2.0 Tutorial [10]を実装します。
UEFNはなんか私の想像していたのとは全く違った未来を歩んでいて、このままいくと先細りして消滅してしまいそうです。
Game系Influencerは嘘つきなんです。
彼らの言う事を信じてその通りにやっても絶対に成功しません。
しかしEpic Games社はこのUEFNで作成したProjectに対して収益の40%を還元するという、一寸とRiskyな決断を下してしまいました。
ただしUnreal Engine 6 = UE5 + Verse [11]によるとUE6にはVerseが標準搭載されるそうです。
のでUEFNの勉強はしておいて損はありません。
またミニゲームなんかを公開しておくのにUEFNは適しているのも事実ですので、そういう意味でも勉強しておくのは損ではないです。
7.1 Make a Book / UI Text In Fortnite - UEFN / Creative 2.0 Tutorial [10]を実装する
<Intro>
特に実装する内容は無いです。
次のStepに進みます。
<Setting up Pop-Up-Dialog Device>
Pop-Up-Dialog DeviceをLevel上に配置します。
これですね。
Pop-Up-Dialog Deviceだと検索にひっかからなくて
Pop-Up Dialog Deviceだと検索で出て来ます。
あ、Hyphenが余計にあった。
はい。
配置しました。
名前をPage1に変更しました。
Response Typeを3 Buttonsに変更します。
TutorialではDefault Back ButtonはButton2に変更していますが、
これは元からButton 2でした。
Button1 Text、Button2Text、Button3 Textの値も以下のように変更しました。
Duplicateして3つに増やします。
表示するTextを追加します。
Page 2とPage 3のTextの内容は指定されていないので以下のようにしました。
<Setting up Page Functionality>
ButtonをLevel上に追加します。
はい。
追加しました。
名前をOpen Book Btnに変更しました。
Page1のShowにこのButtonをBindさせます。
TutorialのImageと微妙に違いますね。
以下にTutorialのShowのScreenshotを示します。
うーん。
多分UIが変更されただけでしょう。
次をやります。
Page2を追加しました。
Page2にあるButton1が押されたらPage1が表示されると言う意味です。
次はPage2の設定を行います。
Page2にあるButton3を押すとPage1に戻ります。
Page2にあるButton1を押すとPage3に進みます。
Pop-Up-Dialog Deviceの設定は以下のようにしています。
Buttonが3つあって、二番目のButtonを押すとDefault画面に戻ります。
それぞれのButtonの名前はPrevious、Exit、そしてNextです。
この設定だとPrevious Buttonを押すと次のPageに移動してしまいます。
あれ?
うーん。
自分の考える正しい方法で実装してみます。
以下のように直しました。
こっちが正しいはずです。
テストして確認します。
<Final Result>
Buttonを押すと以下のUIが表示されました。
NEXT Buttonを押しました。
なんの変化もありません。
あれ?
Previous Buttonを押してみます。
Page2に戻って来ました。
あれ?
先程の設定が間違っていたの?
ああ。
分かりました。
勘違いしていました。
Page1の設定ですが、
OpenBookBtnのInteractを押すとPage1が開きます。
Page2のButton1を押すとPage1が開きます。という意味でした。
うーん。
勘違いしていました。
Testして確認するのは大切です。
Page2とPage3の設定も直します。
これで直ったはずです。
Push Changeを押して
間違いを直します。
もう一回Testします。
NEXTを押しました。
今度はPage2に移動しました。
他のButtonもTestしましたが全部、望んだ動きをしていました。
以上です。
7.2 Deviceの機能を勉強する
今週の勉強である重大な事に気が付きました。
UEFNのFortniteにあるDeviceが
色んな機能を持っていてそれぞれを組み合わせてProgrammingめいた事を実装する事が可能なんです。
というかProgrammingが書けない人達がGameを作成するにはそれしか方法が無いです。
という事はこれらのDeviceの機能を全部勉強してしまえば他のUEFNのCreatorに圧倒的な差をつける事が出来るようになります。
まず今まで勉強したDeviceを以下に整理します。
Player Spawnerが登場しています。
Player SpawnerはしっかりDeviceの中にありました。
Item Granterについて説明していました。
更に以下の方法で武器を実装出来る事も解説されていました。
Item GranterもDeviceにありました。
Buttonもここで出て来ました。
流石にこのDeviceの機能はもう理解しています。
更にEnd Game Deviceと言う名前のDeviceも出て来ました。
これもDeviceにありました。
Guard Spawnerも出て来ました。
敵をSpawnするDeviceです。
しかもここで以下の文章が書かれていました。
やっぱりCreative1.0ではProgrammingを知らない人達がDeviceを組み合わせてGameを作成していたんですね。
ここでItem GranterやEnd Game Deviceの実装をしていました。
Guard Spawnerの実装をしていました。
Prop Moverを勉強していました。
こんなDeviceもありましたわ。
Prop Moverを実際に使用しています。
ここで初めてVerseを使用しています。
VerseのFileはLevel上では以下のDeviceで表示されます。
これも一応Deviceなのでここに記しておきます。
UFO Spawnerを使用していました。
これですね。
UFO Spawnerを実際に使用しています。
こんなのが展開されていました。
Tutorialの勉強をしていますが、そこでCinematic Sequenceが使用されていました。
ここではAssetと書いていますが、Deviceですね。
これですね。
Cinematic Sequenceの使用方法について簡単にまとめていました。
Sports Car Spawnerの使用方法について勉強していました。
これですね。
更にExplosive Deviceの使用方法についても勉強していました。
Sports Car Spawner とExplosive Deviceを実際に使用していました。
以上でした。
<今まで使用したDeviceのまとめ>
以下のDeviceを今まで使用していました。
- Player Spawner
- Item Granter
- End Game Device
- Guard Spawner
- Prop Mover
- UFO Spawner
- Cinematic Sequence
- Sports Car Spawner
- Explosive Device
たった9個のDeviceしかまだ使用していませんでした。
うーん。
これもまたコツコツ勉強するしかありません。
17x11+7 = 194個のDeviceがありました。
更にEarly Accessの中には以下の3つのDeviceもありました。
となると197個のDeviceが存在している事になります。
これの使用方法もぼちぼち覚える必要があるという事ですね。
7.3 Deviceについて更に調査する
まず公式SiteのDevices [12]を見つけました。
軽く読みます。
はい。
Using Devices [13]をみます。
こういう画面があるんですね。
うーん。
どこからこの画面に移動出来るんでしょうか?
すぐ下にこの画面への行き方が説明されていました。
まず以下の編集Modeに行きます。
Tab Keyを押します。
おお、この画面ですね。
Deviceは仕掛けと訳されていますね。
これは普通に装置の方がしっくりくる気がしますね。
Player Spawnerを選択してみます。
右下にPlayer Spawnerの機能が説明されていました。
これだけしか説明されてないのか。
左端にある以下の分類が興味深いです。
開始時の仕掛けを選択したら以下のDeviceが表示されました。
武器の分類に近接が
がありました。
以下の武器がありました。
剣とかあります。
このUIは非常に見やすいですが、UEFNを使用出来るならこっちは触らなくても問題ない気もします。
今週はこれくらいにしておきます。
これらのSiteはこれから少しずつ勉強する事にします。
8. DirectX12の勉強
8.1 Lötwig Fusel氏のD3D12 Beginners Tutorialを勉強する
8.1.1 先週の復習
何をやったのか全く覚えていません。先週のBlogを読み直します。
思い出しました。
先週、Visual StudioのSettingで
というIconがある事が判明しました。
これを押すとTutorialと同じやり方をするだけでTutorialの表示と同じFolderの構成を作成出来そうだという事が判明したんです。
それで
となったんでした。
今週は量が多いですが、これを一気にやってしまいましょう。
8.1.2 Lötwig Fusel氏のD3D12 Beginners Tutorialをやり直す
< Project Setup & ComPointer | D3D12 Beginners Tutorial [D3D12Ez] [14]を実装する>
Blogを見直すと2023-10-29から勉強をしています。この辺を参考にして作り直す事にします。
まずProjectを作成します。Empty Projectを選択します。
この辺は2023-11-05のBlogのやり方と全く同じです。
それで問題ないはずです。
次の設定です。
名前はMyD3D12Ez_2としました。
Solutionが作成されました。
以下のような表示になっています。
げ。
ここまで作成したら2023-11-05のBlogではConsoleのTemplateで作り直していました。
うーん。
流石にここからProjectを作り直すのは面倒です。
Sample Codeを見ると以下のようなFileの設定になっています。
ので以下のようにMain.cppを追加して
更にmain.cppに以下の実装を追加しました。
これを実行すると
となりました。
このProjectで続きを作成しても大丈夫でしょう。
このまま続けます。
Project Settingを変更します。
PlatformをX64に変更します。
うーん。
最初からx64になっていました。
Output Directoryの設定を以下のように変更します。
これで合ってるはずです。
$(SolutionDir)build\$(PlatformShortName)-$(Configuration)\bin\
これをそのままPasteしました。
Editで確認すると
以下のAddressにOutputを作成します。
2023-11-05のBlogではhyphenの意味が分からないと調べていました。
はい。
結果が面白いので、ここにもう一回載せておきます。
Intermediate Directoryの設定を変更します。
以下のAddressをそのままCopyして
$(SolutionDir)build\$(PlatformShortName)-$(Configuration)\obj\$(ProjectName)\
Pasteしました。
Editorで実際のAddressを確認します。
あってそうです。
次にC++ Language Standardの設定を変更します。
しました。
ここで2023-11-05のBlogでは一端、TestしてFolderが指定した場所に作成されるのかどうかを確認しています。
同じようにTestします。
実装しました。
特に問題なく実行出来ました。それでは作成されたFolderを確認します。
まずOutputです。
以下のFolder内に作成されているはずです。
確認します。
に
がありました。
出来ていました。
Intermediateの方も確認します。
となっています。
に以下のFileが形成されていました。
はい。
出来ています。
この時点でSample CodeのFolderと比較してみます。
Sample CodeのFolderです。
私のProjectのFolderです。
Sample CodeにあるGithub関連のFolderやFileを消すと以下のようになります。
まず.vsは同じです。
Buildも同じです。
MyD3D12Ez_2とsrcはほぼ同じ内容でした。
私のProjectではx64というFolderがあります。
X64の中は以下のようになっていました。
これはOutput内のFileと同じです。
要らないので消します。
以下のようになりました。
Sample Codeと比較すると
となります。
これを見るとMyD3D12Ez_2がsrcと同じという事になります。
中身を見てみます。
私のProjectです。
Sample Codeの中身です。
ここにあるFolderは後で追加するものなので今は消しておきます。
X64と言う名前のFolderは何を示しているんでしょうか?
中身を見てみます。
これは以下に示したIntermediateのFolderの中身と全く同じです。
Defaultの設定の時に実行したので、その時に作成されたFolderでしょう。
消します。
これで以下のようになりました。
私のProjectです。
Sample Codeです。
違いはFilters fileの存在だけになりました。
Sample Codeの方にはFilters fileは作成されていませんでした。
これについて少しだけ調べます。
BingのCopilotで聞きました。
XML Fileだそうです。
と言うとTextで開けるの?
開けました。
ああ、これか。
分かりました。
こっちのFilter Viewの管理をしているようです。
成程、Sample CodeはFilter Viewは使用してないので.filterは生成されてないみたいです。
Sample CodeのFilter Viewを見たら以下のようになっていました。
全く整理されていません。
以下の方法でFilter fileを消せると言っています。
これはVisual Studio 2019: use folder structure as filter [15]の解説をまとめていたんですが全く違う話でした。
このFilter fileは後々必要になるかもしれませんので残しておきます。
最後にSample Codeでは
以下に示した
Vcxproj fileやvcxproj.user file、そしてmain.cppがsrc folderに保存されいます。
私のProjectではProject名 Folder内に保存されています。
これの直し方を調べます。
わかりませんでした。
これもこのままで良いです。
Linkingの設定に行きます。
Sample Codeの設定をそのままCopyしました。
2023-11-05のBlogでは以下のようなCommentがありました。
これってDebug Layerと関係あるなんかじゃなかったでしたっけ?
<追記>
これは私の勘違いでした。私が思っていたのはDXGIの事でした。
<>
こんな回答しか得られませんでした。
Debug Layerと関係ありますか?と質問したら以下の回答でした。
あんまり関係なさそうですね。
VC++ DirectoriesのInclude Directoriesの値を変更します。
ここで$(ProjectDir)の値が
私のProjectでは
になっていますが、
Sample Codeでは
になっていました。
こっちはsrcを指定しています。
ここは無視します。
うん。
これで環境設定は完成とします。
一寸まだ不明な点はありましたが、このTutorialをやる分には問題ないでしょう。
次をやります。
Windows.hを追加しました。
ここから2023-11-05のBlogは壮大に勘違いしてFilter ViewでTutorialと同じFilterを作成使用としています。
ここは一からやり直します。
Support Folderを追加します。
今回はしっかりShow All FilesがCheckされた状態で新しいFolderを作成しました。
そのSupport Folder内にWinInclude.h Fileを作成しました。
WinInclude.hの実装です。
更にDebug Layerを使用するために
D3d12sdklayers.hとdxgidebug.hを追加しました。
これでWinInclude.hの実装は終わりだそうです。
Main.cppに戻ってWinInclude.hをIncludeしました。
今度は何の問題もなく実装出来ました。
うーん。
これは全部の実装を一回で終わらせることは無理でした。
今週はここまでにします。
8.2 「DirectX 12の魔導書」の勉強
8.2.1 先週の復習
先週のBlogを読み直します。
「4.5 はじめてのShader」を勉強しています。
Vertex ShaderとPixel Shaderの作成方法を勉強していますね。
正し具体的な方法はBlogにはまとめていませんでした。
ので教科書を見ながら実装していきます。
8.2.2 「4.5 はじめてのShader」を実装する
<「4.5.1 Vertex Shaderの記述」を実装する>
以下のようにしてVertex Shaderを追加しました。
以下のようなFileが作成されました。
関数名をBasicVSに変更しました。
<「Visual Studioの設定」を実装する>
BasicVSを右Clickして以下のBoxを表示します。
更にHLSL CompilerのGeneralを開いて
Entrypoint NameとShader Modelの値を変更しました。
最後にBuildしてErrorが無い事を確認しました。
<「4.5.2 Pixel Shaderの記述」も実装します>
今度はPixel Shaderを作成します。
Vertex Shaderを作成したのと同様にPixel Shaderを作成しました。
PixelShaderの実装もMain()関数をBasicPS()に変更しました。
更にPixel Shaderの設定も以下のように変更しました。
一応、Buildして確認しましたが問題なかったです。
これでShaderの実装は一応出来ました。
8.2.3 「4.6 Shaderの読み込みと生成」を読む
次の節を勉強します。
<「4.6.1 ID3DBlob型」を読む>
ID3DBlob型について解説しています。
ID3DBlob型はShader Objectを扱うための型だけでなく、他のObjectも扱うそうです。
以下のようにShaderのObjectを生成しています。
<「4.6.2 必要なIncludeとLink指定」を読む>
ShaderをCompileするのに必要なHeaderをIncludeします。
更にLibraryもLinkします。
<「4.6.3 D3DCompileFromFile()関数」を読む>
これが結構長い節です。
Shaderを読み込むための関数は、D3DCompileFromFile()関数だそうです。
この節がなんで長いのか分かりました。
このD3DCompileFromFile()関数のそれぞれのParameterについて解説しています。
この部分は今回はSkipします。
<<Errorが起きた場合>>
Vertex ShaderやPixel ShaderのReturn ValueがS_OK出なかった場合の対処方法について解説していました。
Shaderに書いたCodeにErrorがある場合は、ppErrorMsgを見ると良いそうです。
ppErrorMsgに詳細なErrorの情報が載っているそうです。
ただしppErrorMsgの情報を取り出すためには以下のID3DBlogの関数を使用する必要があるそうです。
- GetBufferSize()関数
- GetBufferPointer()関数
ただしこれらの関数はSample Codeでは使用されていませんでした。
うーん。
どうしよう。
一応、教科書には具体的に使用するためのCodeが書かれていました。
次にFile名が間違っている時の例が載っていました。
File名が間違っている場合はHRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)が返って来るそうです。
以上でした。
9. まとめと感想
なし。
10.参照(Reference)
[1] Castle Wall Tool. (n.d.). https://www.youtube.com/playlist?list=PLNbgmFvU__fiPhyUWHHzZ2Nv5ieM_bOdB
[2] Gediminas Kirdeikis. (2023, April 30). Unreal Engine 5 for Architecture - 2023 Full Beginner Course [Video]. YouTube. https://www.youtube.com/watch?v=bT8aSTkpkDY
[3] pinkpocketTV. (2023, December 4). Step-by-Step Unreal Engine 5 tutorial: 3D Gaussian Splatting for Beginners [Video]. YouTube. https://www.youtube.com/watch?v=xdDzChfFY_A
[4] Ben Cloward. (2023, May 4). Using custom HLSL code - Advanced Materials - Episode 24 [Video]. YouTube. https://www.youtube.com/watch?v=qaNPY4alhQs
[5] Rick Banks. (2022, June 20). Houdini - Wall Tool 05 [Video]. YouTube. https://www.youtube.com/watch?v=WE4xrUXnW0s
[6] Rick Banks. (2022b, June 20). Houdini 19 - Wall Tool 04 [Video]. YouTube. https://www.youtube.com/watch?v=AwRKrGL7DmQ
[7] sidefx.com. (n.d.). detail expression function. https://www.sidefx.com/docs/houdini/expressions/detail.html
[8] Joshua M Kerr. (2022, September 25). After effects to Unreal Engine 5 (THE EASY WAY) [Video]. YouTube. https://www.youtube.com/watch?v=r7IW5te_3ZI
[9] Joshua M Kerr. (2022, February 13). TRACK THIS! - Camera tracking from blender to Unreal engine [Video]. YouTube. https://www.youtube.com/watch?v=Ta0am2wC-SI
[10] Pi Equals Three. (2023, April 21). Make a book / UI text in Fortnite - UEFN / Creative 2.0 tutorial [Video]. YouTube. https://www.youtube.com/watch?v=9GPYbD6fnh8
[11] Gamefromscratch. (2024, January 9). Unreal Engine 6 = UE5 + Verse [Video]. YouTube. https://www.youtube.com/watch?v=y87M5ySVGJs
[12] Devices. (n.d.). Epic Developer Community. https://dev.epicgames.com/documentation/en-us/uefn/devices-in-unreal-editor-for-fortnite
[13] Using devices. (n.d.). Epic Developer Community. https://dev.epicgames.com/documentation/en-us/fortnite-creative/using-devices-in-fortnite-creative
[14] Lötwig Fusel. (2023, May 1). Project Setup & ComPointer | D3D12 Beginners Tutorial [D3D12EZ] [Video]. YouTube. https://www.youtube.com/watch?v=rF30qoUFDq8
[15] Visual Studio 2019: use folder structure as filter - Microsoft Q&A. (n.d.). https://learn.microsoft.com/en-us/answers/questions/139429/visual-studio-2019-use-folder-structure-as-filter