1. 今週の予定
今週も予定を見直す時間がありませんでした。ぶっつけ本番でやっていきます。
先週から以下の内容を勉強するようにしました。
- 映像作品としてのLevelの作成
- AIの勉強
- Nvidia Omniverseの勉強
- Gaeaの勉強
- Houdiniの勉強
- UEFNの勉強
- DirectX12の勉強
この結果、あまり無理しないで一週間を過ごす事が出来ました。
今週も同じようにやっていきます。
2. 映像作品としてのLevelの作成
2.1 先週の復習
これから直していきます。
2.2 飛び上がったDragonがもっとCameraに写る様にする
Cameraの位置を変更するとErrorになりました。
代わりにDragonの位置を変更したら別なActorにReferenceしているがそれでも変更するのか。変更したら何かとんでもない事が起きるような警告が出て来ました。
うーん。
じゃ止めます。
かわりに新しいLSを作成してそこで試す事にします。
Camera Workはこれくらいで十分でしょう。
次はDragonの色を調整します。
前回どうやってDragonの色を調整したのかを確認します。
うーん。
Directional Lightの色を調整しているだけみたいですね。
取りあえずこれをやってみます。
2.3 Dragonの影を地面に映す
これは道路にPlateを追加すれば、Dragonの影が写る事になります。
Plateを追加します。
取りあえず上記のようにPlateを追加しました。
Dragonの影が追加されています。
Plateの位置をもう少し調整します。
3D Gaussian Splattingは3DのObjectを配置するとある角度から見た場合しか整合性が取れません。
以下のように
この角度からみると道路に沿ってPlateが配置されていますが、
上から見るとこんな感じです。
うーん。
これは結構合っていますね。
そういう時もあります。
影の様子を確認するためにPlateは地面より少しだけ高い位置に配置しています。
Cameraから見ると以下のようになっています。
この位置から見るとPlateは地面と同じ位置に配置されていますね。
こういうのが3D Gaussian Splattingに3DのObjectを配置すると普通におきます。
Plateの色を灰色にします。
以下のMaterialを作成しました。
ここからMaterial Instanceを作成して
このMaterial InstanceをPlateに適用します。
しました。
まだ真っ白ですね。
Base Colorを調整します。
以下の色までBrightnessを下げると地面とほぼ同じ色になりました。
ほとんど黒です。
ぱっと見では地面は黒くは見えません。
うーん。
光が当たる前の物の色って実際に見える色とは結構違うみたいですね。
取りあえず影ははっきり見えるようになりました。
でも影がぷっつり切れている感じがあります。
角度を変えて確認しました。
ぷっつり切れています。
次に以下のSceneです。
Dragonが飛び去った後ですが、Plateの灰色が道路全体に映し出されるので、画像が単調に見えます。
この時にはPlateは要らないので消してしまいたいです。
そうだ。Level SequenceでAssetのVisibilityを変更する方法が分らないんだ。
これを調べる必要があります。
後は、このPlateのMaterialに前作成したPwnisher氏のMaterialとMegaScanのMaterialを組み合わせた自作のMaterialを使用する事ですか。
こういうやつですね。
2024-07-22のBlogを見ると
木の部分にRandomな劣化を追加する事でPhoto-realisticな質感を再現しています。
こういう技術が軽く使用出来るようになりたいですね。
今週はこれくらいにしておきます。
2.4 来週の予定
忘れないように来週の予定だけ書いておきます。
- Plateを追加してDragonの影全体が道路に写るようにする。
- Level Sequence上でPlate(Asset)のVisibilityを調整する方法を調査する
- PlateにPwnisher氏のMaterialとMegaScanのMaterialを組み合わせた自作のMaterialを使用する
以上の事を来週はやります。
3. AIの勉強
3.1 Lecture1の宿題をやる
先週までの勉強でやっとFast.aiを使用したAI学習による画像の区別のやり方が判明しました。
これでやっとJeremy Howard氏の Practical Deep Learning for Coders: Lesson 1 [1]の宿題のやり方が判明しました。
ので今回はこれをやる事にします。
<何と何を比較するか?>
BirdとForestのような簡単で身近な2つのImageを比較したいと思います。
はい。決めました。
Charizard(リザードン)とMew(ミュウ)を勉強させてこの2つの区別がつくようにします。
GoogleでCharizardを検索しました。
結果です。
うーん。
いいんじゃないでしょうか。
今度はMewを検索しました。
こっちもよさそうです。
次は以下の部分の言葉を決定します。
Photoは存在しないので
AnimeとかCartoonにしますか。
Charizard+AnimeでGoogleで検索すると
以下の画像が表示されました。
これはイイ感じです。
大体良かったんですが、検索結果に別なAnimeが混じっています。
Tokyo Mew MewというAnimeがMew Animeで表示されていました。
成程、Pokemonと言うWordを追加してみます。
今度はMewを表示されました。
けど変なImageも混じっています。
まあ試しにやってみましょう。
最初に含むWordはphotoの代わりにPokémonにします。
次のWordはsun photoの代わりに
AnimeかCartoonにします。
これは後で決定します。
最後のWordはShade photoの代わりに
うーん。
Card Pokémonとかにしますか。
Charizardは良いんですが、Mewは変なImageが検索結果に混じっています。
駄目かもしれません。
そうだ。
MewじゃなくてPikachuにします。
Pikachu Pokémonの検索結果です。
これならいけるでしょう。
Pikachu Pokémon Animeの検索結果です。
一個、Pichu (ピチュー)が混じっています。
うーん。
まあ良いか。
Pikachu Pokémon Cardも検索してみます。
うん。
大丈夫そうです。
Charizard Pokémon Cardも検索してみます。
うーん。
こっちもいけそうか?
ぬいぐるみ(plushie)でも検索してみます。
Pikachu Pokémon Plushieで検索しました。
こっちの検索結果は正確です。
Pikachu以外のものは表示されていないです。
Charizard Pokémon Plushieを検索します。
ああ、大体あってるけど
Charizardの帽子をかぶったPikachuがいる。
しかももう一体ありました。
うーん。
一回試しにやってみます。
Downloadする3つのWordは以下のようにします。
- Pokemon(もしくはPokémon)
- Pokemon Anime、もしくはPokemon Cartoon
- Pokemon Plushie
これで準備は出来ました。
Colabを起動してやってみます。
まずduckduckgo_searchが使用出来るようにInstallします。
結果です。
正常にInstallされたようです。
次にAIにPikachuかCharizardかを判断するためのImageを一枚作成します。
以下の2つの実装を参考にして作成します。
最初の実装はBird Photoと言う検索Wordを使用して1枚のImageのURLを保存しています。
次の実装はそのURL上にあるImageにBird.jpgと言う名前を付けて256pxで保存しています。
まず最初の実装を改良してPikachuのImageのURLを一個確保しましょう。
検索するWordをPikachu Pokemonに変更しました。
実行します。
結果です。
URLを見てみます。
PikachuのImageがありました。
あってますね。
次にこのURL上にあるImageにPikachu.jpgと言う名前を付けて256pxで保存しています。
これは先程の2番目のCodeを利用して作成します。
これはBird.jpgの名前をPikachu.jpgに変更したら良いのか。
しました。
実行します。
おお、PikachuのImageが表示されました。
これでPikachu.jpgが保存されたはずです。
今度はAIにPikachuとCharizardの違いを勉強させます。
まずは
学習用のImageをDownloadするための実装をします。
以下のCodeを改良して作成します。
以下のように改良しました。
Path()関数の値がこれでいいのか一寸不安です。
Errorが起きた時はここから確認する事にします。
実行します。
なんかWarningが出ています。
うーん。
どうやってRGBA Imageに変換するの
後、Palette images with transparency in bytesってどんなFormatを指しているの?
よく分からないけど、
これは無視して先に進みます。
もし駄目だったら後で考える事にします。
次に以下のCodeを実行します。
これでもしかしたらさっきの駄目なImageは外してくれるかもしれません。
一枚だけ外されました。
次は以下のCodeを実行します。
Data Blockを作成します。
この中身の構造については忘れてしまいましたが、このままCopyして実行して問題ないでしょう。
実行しました。
結果です。
おお、あってますね。
では以下のCodeを追加してAIに学習させましょう。
学習が完了しました。
Fine TuneのError Rateの2番目が0.03になっていますね。
それ以外は良いんじゃないでしょうか。
はい。
ではAIの学習成果を確認します。
先程のPikachuのImageを見せてPikachuかCharizardが判別できるかテストします。
以下の実装を改良して判別用のCodeを作成します。
以下のように改良しました。
実行します。
お、Pikachuと返事が来ました。
がProbabilityが0.0003です。
つまりPikachuでないと言っています。
はあ?
駄目じゃん。
じゃ、今度はplushiesをCartoonに変更して試してみます。
結果です。
失敗したImageを取り除きました。
Data Blockを作成しました。
学習させました。
今度はError Rateが0です。
上手く行くでしょう。
結果です。
あれ?
Probabilityが0.0001です。
何故か逆?
じゃ今度はPikachu.jpgに以下のCharizardのImageを入れてテストしてみます。
以下の結果が表示されました。
あ、分かりました。
ImageがCharizardの時に1になるんです。
以下のように書き直しました。
結果です。
最初のPikachuのImageでテストしてみます。
出来ました。
4. Nvidia Omniverseの勉強
先週の最後で以下のように書いていました。
もう今週はこれだけです。
どうやったらOmniverse CreateのProjectをSave出来るかを調べるだけです。
4.1 Omniverse CreateのProjectをSave出来るようにする
取りあえず、Omniverse Launcherを起動させます。
LoginするのにPasswordの入力とか聞かれました。
DriveのLoginは表示されなかったです。
Driveは既に起動NvidiaのFolderは相変わらずLockされていましていました。
Createを開きます。
普通ですね。
いきなりSave出来ました。
新しいProjectでもう一回試してみます。
Cubeを追加しました。
これをSaveします。
まずFileからSaveを選択します。
以下のBoxが表示されます。
ここでBoxの一番下の部分に以下の設定をします。
File NameにこのProjectの名前を入れます。ここではTest1とします。
次にFileのTypeを選択します。ここではProjectを選択しました。
次にFileのTypeを選択します。Usdを選択しました。
そしてSaveを押します。
これでSave出来ました。
以下のようにSaveされていました。
はい、出来ました。
4.2 Createをもう少し勉強する
Sketchfabから以下のObj型のModelをDownloadしました。
これをOmniverse Create内で開いてみます。
以下の場所にOBJ fileとして存在していますが、引っ張れません。
うーん。分からん。
Setting Up A Project in NVIDIA Omniverse Create Part 1: Getting Started [2]を見たら、USD Fileの場合は左ClickしながらDragしろって言っています。
ただしUSD File出ない場合は、右Clickして以下のBoxを表示してUSD Fileに変換しろって言っていました。
うーん。
流石にこのやり方は覚えていましたが、右Clickして表示されたBox内にUSDへの変換はありませんでした。
もしかしたらObj Fileだから駄目なのかもしれません。
FBX Fileで試してみます。
さっきの車のSiteに行ったらそもそもFBX Fileは無いです。
うん。
代わりにUSDZというFileがありますね。
これってUSDの派生形な気がします。
これを代わりにDownloadして試してみます。
USDで表示されていますが、鍵がかかっていますね。
えい。
Drag and Dropしてしまえ。
出来ました。
おお。
でも環境が真っ黒なんで車はよく見えません。
2024-06-23のBlogでSetting Up A Project in NVIDIA Omniverse Create Part 1: Getting Started [2]を勉強しているのでどうやって環境の設定をするのかを確認しました。
してなかったです。
環境の設定はこの次のTutorialで勉強するそうです。
うーん。
そうなのか。
じゃ、今週のOmniverseの勉強はここまでとします。
来週は次のTutorialであるSetting Up A Project in NVIDIA Omniverse Create Part 2: Lighting & Rendering [3]を勉強します。
5. Gaeaの勉強
今週はGaea2の勉強を行います。
5.1 Gaea2.0のGroupを確認する
まずNodeのGroupが以下のように変化しました。
PrimitiveとTerrainが分けられていますね。
Surfaceの後にSimulateがあります。
そしてSimulateの中にErosionがあります。
これはGroupの順番がTerrainの作成の順番になったという事です。
Gaea1ではErosionの後に、LookDevがありました。
これはErosionの設定をした後で、LookDevの設定をすべきとGaeaが勧めているようで、非常に気になっていた部分です。
これがGaea2ではSurface、そしてSimulateになったのは、私の考えが正しかった事をQuadSpinner社が認めた感じがしてうれしいです。
それぞれのGroupについて簡単に見ていきます。
<Primitive>
Primitiveは更に以下のSub Groupに分かれていました。
<<Assets>>
Assetsには以下の3つのNodeがありました。
Fileノードです。
このNodeはHeight MapをImportするNodeでした。
試しに前のProjectで生成したHeight MapをImportしてみました。
こんな感じでImport出来ました。
次のObjectノードですが、
このNodeはまだ作成中で使用出来ませんでした。
最後のTileInputノードです。
このNodeはどんな機能なのか不明です。
Propertiesの三本線のIconのところに、Show Help for this Nodeがありますが、
これを選択しても何も表示されません。
<<Basic>>
ここにはConstant、Noise、Perlin、そしてVoronoiがありますね。
Gaea1のPrimitiveは以下のようになっていました。
これが更に分類された感じですね。
ここにあったPrimitiveの内、基礎的なものがGaea2ではBasicに分類されたみたいです。
お、ここにTileInputがありました。
という事はGaea1ならTileInputノードのDocumentはあるという事か。
一応調べておきます。
DocumentにはTileInputノードの解説は無かったです。
Extendedです。
うーん。
どれも知らない。
DriftNoiseノードとMultiFractalノードは、Gaea1にもありますね。
後、WaveShineノードに似た名前のShinyWavesノードがありました。
<<Gradient>>
PrimitivesのSub Groupの最後であるGradientです。
ここには以下のような形状を生成するNodeが集められていました。
Base Lineとして使用するのでしょうか?
<Terrain>
次のGroupはTerrainです。
ここはGaea1のGeo PrimitivesのNodeが配置されてるはずです。
Terrainは以下の2つのSub Groupに分けられていました。
<<Landscape>>
Landscapeに属してるNodeです。
何とMountainノードがありません。
<<Primitives>>
こっちにMountainノードがありました。
あれWorselandノードが無くなってる!
結構、お気に入りのNodeだったのに。
<Modify>
Modify Groupです。
以下に示したAdjust、Blur、Effect、Profile、Transform、そしてWarpのSub Groupが存在しています。
Gaea1のWarps、Adjustments、Profile、そしてFilters Groupを
合わせて一つのGroupにした感じでしょうか?
Subgroupの中身を見ていきます。
<<Adjust>>
Adjustの中身です。
AdjustノードはFxノードの別名ですね。
AutolevelノードからClipノードはFxノードの機能を分割して表示しているだけですね。
Caea1ではFxノードには以下の機能がありました。
この中にある機能でGaea2のFxノードになさそうなのはLogとBlurですか。
逆にGaea2で新しく追加された機能っぼいのは、Denoise、Extend、Match、そしてTransposeのようです。
いやこれはGaea1のAdjustments GroupにあるNodeと同じと考えるべきなのか。
こっちと比較するとDenoiseノード、Transposeノードはありました。
<<Blur>>
Gaea2のBlur Groupです。
Gaea1ではBlurノード一個だったのがGaea2では4つに増えています。
<<Effect>>
Gaea2のEffectです。
Origamiノードとか何に使うのか前から分かりません。
Swirlノード位ですね、ここで使用するかもしれないNodeは。
<<Profile>>
ここは何するためのNodeを集めたのかよく分かりません。
以下に示した様にProfile GroupはGaea1からありました。
うん。
Terraceノードがここに配置されている。
TerraceノードはSurface (Gaea1のLookDev) Groupに属してると思っていたんですが。
ここはよく分からないのでPassします。
<<Transform>>
以下の3つのNodeがありました。
TransformノードはMountainノードの位置を移動させるのによく使用しました。
後の2つのNodeはよく知らないです。
Nodeの計算結果を移動させるNodeを集めた感じでしょうか?
<<Warp>>
最後のWarpです。
Warp Group自体はGaea1にもありました。
Warpノードは使った記憶はありますが、どんな機能だったのかは覚えていません。
結構長くなってしまったので残りのGroupの調査は来週やる事にします。
今週のGaeaの勉強はここまでとします。
6. Houdiniの勉強
6.1 先週の復習
Resampleノードの機能について勉強していました。
勉強した内容で重要だと思ったところを以下にまとめます。
まず以下のHelpの存在です。
Houdiniから機能を調べたいNodeを選択した状態で上記のIconをClickすると上記のWindowが開きます。
ここからNodeのDocumentを見た場合、Netで検索するのとは違い、
以下のExamplesを実際にHoudini上で試す事ができます。
こんな感じです。
次にこのExamplesからLoadを押して表示したSamplesについてです。
これがかなり分かり易かったです。
ここではこのNodeの肝となる部分が具体的な例が表示された状態で説明されていました。
例えば先週のResampleノードの場合ですと、
MethodをEven Length Segmentsにした状態でMeasureをAlong Arcにした場合とAlong Chordにした場合や
Maximum Segment Lengthを選択した場合とMaximum Segmentを選択した場合の
違いなどが論じられていました。
つまりこのSampleを見る事で、学習者はResampleノードではこれらのParameterが重要である事を知る事が出来ます。
ちなみにAlong ArcとAlong Chordの違いについては以下のようにまとめてありました。
6.2 次に勉強するNodeを調べる
完全に理解した訳では無いですが、Resampleノードについての勉強はこれくらいで十分でしょう。
次のNodeを勉強する事にします。
次にある知らないNodeはCopyToPointノ-ドでした。
あ、これは既に勉強していました。
となると
その次のNodeはResampleノードでした。
そしてその次のNodeがPolyFrameノードです。
これは知らないNodeです。
今週はPolyFrameノードについて勉強する事にします。
6.3 PolyFrameノードについて(過去のBlogの内容を復習する)
このNodeについて勉強する前に過去のBlogを読み直してこのNodeを何時使用したのかについて確認します。
2023-11-05のBlogで勉強していました。
これを読むとPolyFrameノードは、それぞれのPointのTangentを表示するために使用したようです。
PolyFrameノードのParameterの設定は以下のようにしていました。
うーん。
これを見るとそれぞれのPointのTangentの名前をNに指定するために使用しているように見えます。
よく分かりません。
2023-11-12のBlogも確認しておきます。
PolyFrameノードの実装をしているはずです。
これを読むと以下のように書いてありました。
PolyFrameノードを実装したけどなんの変化も起きなかった。
それぞれのPointのTangent Lineは以下のIconをEnableする事で表示する事が出来た。
と書かれていました。
何故かその次の週である2023-11-19のBlogでもPolyFrameノードについて書かれています。
一応、読んでおきます。
何と、この週のBlogでは、PolyFrameノードの機能がよく分からないので調査していました。
危ない。
同じ勉強を2回繰り返すところでした。
このBlogの内容をじっくり読み直します。
まず以下に示した様にDocumentに当たっています。
この回の説明を読むと、そもそもHoudiniのNodeのDocumentを読むのが初めてらしく、Documentがある事を発見して喜んでいました。
PolyFrameノードの機能については
と書かれていました。
しっかり勉強していました。と思ったら、すぐその後で
と述べて、ここで勉強を打ち切ってしまいました。
その後は、PolyFrameノードのDocumentをざっと読んで、Documentの構成を理解して終わっていました。
うーん。
PolyFrameノードの機能を理解したとは言えませんね。
やっぱり今回、しっかり勉強する必要がありました。
6.4 勉強の指針を振り返る
ここまでPolyFrameノードの機能について調べて何なんですが、Castle Wall ToolのTutorialの勉強が終わった後で、これからのHoudiniの勉強の指針についてまとめた記憶があります。
まとめた記憶はあるんですが、具体的な内容までは覚えていません。
PolyFrameノードの勉強を続けて良いのか不安になって来たので、Houdiniの勉強の指針をまとめたBlogを読み直して確認する事にします。
うわああ。
これをやる事に決めたんでした。
Pointの生成方法は先週までの勉強で理解したので今週はその次の「2.PointからLineを生成して面を作成し、その面から立体を作成する」を勉強しなければなりません。
うーん。
仕方ない。
ここまでPolyFrameノードについて調べてしまったので、こっちを先にやります。
その後で「2.PointからLineを生成して面を作成し、その面から立体を作成する」を勉強します。
6.5 PolyFrameノードについて
それではPolyFrameノードの機能について調べていきます。
PolyFrameノードを選択してHelpを押しました。
以下のDocumentが表示されました。
もう一回このDocumentを読み直します。
ぱっと読みましたが、ResampleノードのようなExamplesは提供されていませんでした。
機能についてですが、以下の説明が全てを語っています。
「This node generates coordinate frame attributes for points and vertices based on a user selected frame-style.」
しかしこれを読むと
Coordinate Frame Attributesって何?
と言う2023-11-19のBlogと同じところで訳分からなくなってしまいます。
Coordinate Frame Attributesについて調べる事にします。
検索しても出て来ません。
Copilotに聞いてみます。
以下の回答を得ました。
Coordinate Frameの意味が判明しました。
3D ModelとかでそれぞれのPolygonやVerticesのTangent Lineとかを表すのに、どのCoordinateを使用するかが問題になります。
Coordinate Frameの場合はLocal Coordinate Systemを使用しているそうです。
そしてそのCoordinate System内で表示される沢山のVectorを総称してCoordinate Frameと呼ぶそうです。
PolyFrameノードにおけCoordinate Frame Attributesの例としてNormal VectorやTangentが紹介されていました。
つまり単にNormal Vectorを追加するって意味だったんです。
試してみます。
以下のようにPolyFrameノードを追加して
Parameterを以下のように変更しました。
これは2023-11-05のBlogの設定です。
この設定だとNormal VectorではなくTangent Lineが追加されると思われます。
Scene Viewの以下のIconをEnableさせます。
結果です。
Tangent Lineは表示されません。
あれ?
Castle Wall ToolのProjectで確認します。
Polyframeノードを可視化しました。
結果です。
それぞれのPointのTangent Lineが可視化されています。
PolyFrame1ノードの一つ上のNodeであるResampleノードを可視化してみました。
結果です。
Tangent Lineはありません。
やっぱりPolyFrameノードがTangent Lineを追加しています。
もう一回先程のProjectを開いて確認したらTangent Lineは表示されていました。
線が細くて見逃していました。
拡大するとTangent Lineが表示されているのが分かります。
その前のCopyToPointノードを選択して可視化すると
以下のようになっています。
拡大して見ると
Tangent Lineはありません。
はい。
今度は確認出来ました。
6.6 「PointからLineを生成して面を作成し、その面から立体を作成する」を勉強する
まずPointからLineを生成する手法を復習します。
以下のTutorialでBrickをPointから作成しています。
2023-12-10のBlogでHoudini 19 - Wall Tool 04を勉強していました。
以下の部分でPointから立体を作成する方法を勉強していました。
この部分をまとめ直します。
LineをPoint間で切っています。
Convertlineノードを追加しました。
このNodeでLineをPoint間で切るそうです。
使用してLineをPoint間で切りました。
次にFor-Each Primitiveノードを追加しました。
ここから分割したLine一つずつに対して何かしていくって事でしょうか?
Primitivesって事はLineに対して何かするって意味ですよね。
Foreach_beginノードを可視化します。
これってPointだけが表示されてるの?
でも0と1の2つのPointが表示されていますね。
よく見るとLineも表示されていますね。
という事はLineとその両端にあるPointを含めて一個のPrimitiveとして存在しているという事になります。
今度は以下に示した様にforeach_beginノードの結果とLineノードの結果をCopyToPointノードで混合しています。
その結果、以下の結果になりました。
これは、最初に勉強したCopyToPointノードの使用方法と同じです。
CopyToPointノードは最初のLineのそれぞれのPointに次のLineを追加しています。
簡単に説明します。
以下のLineがあると思われます。
それぞれのPointに2番目に指定されたLineを追加します。
最終的には以下のようになります。
はい。
今度はこの2つのLineを利用して面を作成します。
今度はSkinノードを使用していました。
結果です。
最後にこの面を立方体にします。
使用するNodeはPolyextrudeノードです。
結果です。
これでBrickが生成されました。
Loopの最後は以下のようになっていました。
はい。以上でした。
6.7「PointからLineを生成して面を作成し、その面から立体を作成する」のまとめ
以下に今まで使用した事のないNodeでこの作業で使用したNodeをまとめます。
- Convertlineノード
- For-Each Primitiveノード
- Skinノード
- Polyextrudeノード
それぞれのNodeがどんな目的で使用されたのかを以下にまとめます。
<Convertlineノード>
LineをPoint間で切って、Point間で一つのPrimitiveを生成するために使用していました。
<For-Each Primitiveノード>
Convertlineノードで生成したPrimitive一個一個を加工するためにこのNodeを使用していました。
<Skinノード>
以下に示したPrimitiveから面を作成するために使用していました。
こんな結果になります。
<Polyextrudeノード>
Skinノードが作成した面をExtrudeして立方体を生成しています。
以上です。
「PointからLineを生成して面を作成し、その面から立体を作成する」ための実際の手法は大体判明しました。
来週は実際にやってBrickが生成出来るか試してみます。
7. UEFNの勉強
7.1 先週の復習
先週はClassの生成方法、つまりClassの作り方、ClassからInstanceを生成する方法、Classを継承して新しいClassを生成する方法を勉強しました。
以下にそれぞれのやり方を簡単にまとめます。
<Classの作り方>
以下に例を示します。
クラスの名前を書き、
:=class:
と書きます。
改行してIndentを追加しそのClassのMemberを追加します。
MemberにはPropertyとFunctionがあります。
PropertyはValueを指定するもので一つの変化しない値を指定するためのConstantと値を変化する事が可能であるVariableがあります。
Constantの指定方法は
名前:Type
です。
Variableの指定方法は
var名前: Type = Default値
FunctionはMethodとも呼ばれ指定する方法は以下のようになっています。
Function名() : Return Value = 別なFunction
これは例としてはあまり適切ではなかったですね。
一からFunctionを作成する方法も後でまとめる事にします。
後、ここでSpecifierを使用している関数を使用する事で、この関数にそのSpecifierの機能を追加する事も可能だと述べていました。
Specifierに関しては、先週のBlogでは
と書かれていました。
Specifierの定義というよりはConvergesとNativeの関係がよく分からないと言いたいみたいですので、これは今週調査する事にします。
<ClassからInstanceを生成する方法>
以下に例を示します。
以下のようになっていました。
Instance名 := Class名{Property名:= 値}
ああ、分かったC-likeな言語では=で表すところをVerseでは:=で表しているんだ。
x=x+1
という悪名高いProgramming言語の文法を
x:= x +1
にする事で初心者にも受け入れられやすくしたんだ。
成程。
納得しました。
<Classを継承して新しいClassを作成する方法>
以下に例を示します。
以下のように作成します。
Class名 := class(継承するClass名):
Classを作成する時は行の最後に:があるんですね。
追加するPropertyやFunctionをその後に書きます。
書き方はClassの作成と同じです。
<Blogの内容>
NPC_Behaviorでは以下の書き方でInstanceを作成していました。
以下の手順でInstanceを生成していました。
Instance名:Class名 = Class名{Property名=値}
以下のやり方と同義なはずです。
Instance名 := Class名{Property名:= 値}
あ、そう言う事なのか。
さっき以下のように書きましたが
こういう考え方が絶対間違っているとは言えませんが、
どっちかというと
Instance名:Class名 = Class名{Property名=値}
の短縮形が
Instance名 := Class名{Property名:= 値}
になったと言えそうです。
先週のBlogでは以下のように書いてありました。
今週も少しだけ探してみる事にします。
あ、これ忘れてた。
今週はPropertyって呼んでました。
Dataと呼ぶ事にします。
以下の疑問が書かれていました。
ChannelはDataなので値を指定する必要があります。
そのやり方が
Channel := ○○
となっている事に疑問を抱いています。
Channel =○○
が正しいんじゃないのか?と書いています。
ChannelというVariableかConstantは既に宣言されているので、その値を指定するだけです。
ので=を使用するのが正しいのではないかという質問です。
これも今週調べる事にします。
先週のBlogではここで先々週のBlogで調べていたDebug Draw Classを調べています。
で以下の部分の実装が理解出来ないので
この部分は来週勉強すると書いて終わっていました。
これも今週、勉強する事にします。
<今週、やる内容>
先週のBlogを読み直した結果、今週、調査、勉強する必要がある内容が大体判明しました。
以下にまとめ直します。
- 一からFunctionを作成する方法
- Specifierの定義(ConvergesとNativeの関係)
- Instanceの作成方法が2つある件
- Instanceを生成する時にParameterの指定を、Data名:=値、と書く件、Dataは既に宣言されているのだから:=でなく=を使うべきではないのか?
- Debug Draw ClassのCodeをよんで理解出来るようにする
以上です。
7.2 今週、調査する課題をやる
<一からFunctionを作成する方法>
公式SiteのFunctions [3]に詳しい説明が書かれていました。
以下にFunctionの例を示します。
その構造は、
Function名(Parameter名:ParameterのType):Return ValueのType =Functionの機能の実装、無い場合は{}で代用可能
となっています。
Parameterには以下のように書く事も可能です。
それぞれの意味は以下のようになっています。
Functionの呼び方です。
以下のようなFunctionがあると仮定して説明しています。
以下のように呼び出します。
ふーん。
結構複雑ですね。
まずX:intのように?がついてないParameterの場合は、値だけを指定しています。
次に?Y:intのように?がついているParameterの場合、?Y := 2のようにどのParameterの値なのかまで指定しています。
?がついているParameterの場合はParentheses内での順番は関係ないようです。
最後に?がついていてDefault値を指定している?Z: int = 0のようなParameterの場合は、指定しなくても良いし、指定する場合は?Z := 5のような指定をすれば良いみたいです。
<Specifierの定義(ConvergesとNativeの関係)>
先週、以下のように書いていました。
まずはConvergesの定義を読みます。
FunctionがNativeであるときだけ、Convergesが現れると書いています。
という事はConvergeがあるFunctionはNativeである事は間違いないようです。
Nativeの定義も調べます。
Specifiers and Attributes [4]のImplementation Specifiersの項に以下のように書かれていました。
ようするに元のCodeがC++で書かれている関数の事です。
<Instanceの作成方法が2つある件>
これです。
これは別にこの書き方に不満がある訳では無く、この書き方を正式なSiteで述べている箇所を見つけたいだけです。
やっぱり見つからないです。
<Instanceを生成する時にParameterの指定を、Data名:=値、と書く件、Dataは既に宣言されているのだから:=でなく=を使うべきではないのか?>
これはそういうRuleだと覚えておきます。
この辺は深堀しても何も得る事はなさそうです。
<Debug Draw ClassのCodeをよんで理解出来るようにする>
そうだ。
先週開けなかったUEFNを開いてNPC_BehaviorのCodeを確認してみます。
何と、VerseのCodeがErrorになって動きません。
以下の実行のNavResultGoToNextがErrorになっていました。
取りあえず関連するCodeをComment Outしておきます。
やっぱり先週Visual Studio Codeから勝手に開いたのが良くなかったですね。
新しくNPC_BehaviorのVerse fileを作成しました。
こっちのCodeを参照する事にします。
一応、CD_MyFirst_NPCの
NPCBehavior Scriptの設定だけは新しいVerse Fileに変更しておきます。
この辺の詳しい設定方法は2024-07-14のBlogにまとめてあります。
来週、この辺はやり直す事にします。
もう気力がなくなって来ました。
今週のUEFNの勉強はここまでにします。
8. DirectX12の勉強
8.1 Lötwig Fusel氏のD3D12 Beginners Tutorial [D3D12Ez]を勉強する
8.1.1 SwapChain | D3D12 Beginners Tutorial [D3D12Ez] [10]の最後の10分を実装する
CreateSwapChainForHwnd()関数のParameterにあるもう一つのStructである
DXGI_SWAP_CHAIN_FULLSCREEN_DESC型のsfdの要素を指定します。
Sfdの要素で指定する必要があるのはWindowedだけだそうです。
2023-04-17のBlogを見ると「DirectX 12の魔導書」でSwapChainの生成について勉強しています。
ここではDXGI_SWAP_CHAIN_FULLSCREEN_DESC型のParameterは使用していません。
Nullptrが代わりに使用されていました。
Shutdown()関数にSwapChainをReleaseするための実装を追加します。
DXWindowのH Fileの方に新しい関数、Rreset()を追加します。
Window Cpp Fileに以下の実装を追加します。
SwapChainにあるPresent()関数を使用しています。
これはどんな機能なのか調べていませんでした。
後で調べる事にします。
以下に示した様にMain()関数内にPreset()関数を追加します。
この状態で実行するとWindow内が黒く表示されるようになるそうです。
試してみます。
おお、黒くなっています。
更にWindowの大きさを変更しても黒いままになりました。
(Screenshot Upload出来ず)
後、GPUを使用している事を示すお知らせが表示されました。
ここで先週のBlogでは以下のように書かれていました。
うーん。
Errorなんか起きてる?
起きていました。
先週のBlogを読み直したら、このErrorの直すための作業と何故その作業をするとこのErrorが直るのかについて延々と語っていました。
全く覚えてなかったです。
その内容ですが、非常に複雑でこれ今軽く復習した位じゃ理解出来なそうです。
一寸ずつやって行くことにします。
まずはErrorのMessageを以下に示します。
D3D12 ERROR: ID3D12Resource2::<final-release>:
CORRUPTION: An ID3D12Resource object (0x000002438D924E50:'Unnamed Object') is referenced by GPU operations in-flight on Command Queue (0x00000243875F59F0:'Unnamed ID3D12CommandQueue Object').
It is not safe to final-release objects that may have GPU operations pending.
This can result in application instability.
[ EXECUTION ERROR #921: OBJECT_DELETED_WHILE_STILL_IN_USE]
Exception thrown at 0x00007FFF55C8FABC (KernelBase.dll) in MyD3D12Ez_2.exe:
0x0000087D (parameters: 0x0000000000000000, 0x0000004DCDCFCD20, 0x0000024385234090).
Unhandled exception at 0x00007FFF55C8FABC (KernelBase.dll) in MyD3D12Ez_2.exe:
0x0000087D (parameters: 0x0000000000000000, 0x0000004DCDCFCD20, 0x0000024385234090).
先週のBlogの内容と一緒ですね。
まずこのErrorが起きた原因を以下にまとめます。
SwapChainをReleaseしたからこのErrorが発生した訳ですが、SwapChainでは以下のDataをReferenceしています。
- Two Buffer
- Two ID
- Three Digital Resource
そしてこれらのDataはCommand QueueからもReferenceされています。
Command QueueからReferenceされているって事はGPUから使用される可能性があるDataって事になります。
ここでErrorのMessageの意味がはっきりと理解出来るようなる訳です。
An ID3D12Resource object is referenced by GPU operations in-flight on Command Queue
で解決策なんですが、
SwapChainをReleaseする前にCommand QueueをFlushすれば良いそうです。
ところが、このCommand QueueをFlushするのが結構大変です。
以下のようにやるそうです。
まずFlushするためにはSignalAndWait()関数を呼ぶ必要があるそうです。
これが何故かについては先週のBlogでもまとめてなかったです。
のでこれはそういうものだと納得して先に進みます。
SignalAndWait()関数ですが、作成されたCommand Queueの数だけ呼ぶ必要があるそうです。
のでFor Loopで何回も呼び出しています。
うーん。
作成されたDataの数、つまりBufferの数だけCommand Queueを呼ぶのでしょうか。
BufferとCommand Queueの関係を忘れてしまっています。
これは後で調べます。
では実装していきます。
まずFlushする時期ですが以下の場所になります。
Main.cpp内のWhile Loopが終了した後ですが、Shutdown()関数が呼ばれる前です。
次にWindow.cppに以下に示した様にGetFrameCount()関数を作成します。
既に追加されていました。
Tutorialでは以下のような実装になっていたんですが、
Sample CodeではFrameCountの変数とFrameCountの関数は別々に作成されていました。
ここではSample Codeのやり方を真似しました。
SwapChainを作成するための関数のParameterの一つであるSwdのBufferCountの値をGetFrameCount()関数に変更します。
これは別にCommand QueueをFlushするのとは関係ないですが、前にこの値の指定方法がSample CodeとTutorialで違っていたのを大騒ぎしていたので、一応ここで直した事を記録しておきます。
後、先週のBlogでGetFrameCount()関数の実装を
Window.cppに追加すると書いていましたが、実際はWindow.h Fileに追加します。
これは間違いです。
次に以下に示した実装をDXContext.h fileに追加します。
これは既に追加していました。
DXContext.hを作成した時Sample CodeをそのままCopyしたのでその時に作成したと思われます。
後、先週のBlogではContentと書いていました。
Contextが正しいです。
最後にMain.cppのFlushのところにこの関数を呼び出すCodeを追加します。
これでSwapchainをReleaseする時には、SwapChainから参照しているDataが、Command Queueからも参照されている事は無くなっているはずです。
テストします。
Errorが消えました。
先週のBlogだと以下のWarningが出ると書いていましたが、
出ませんでした。
更に先週のBlogを読むと以下に示した様に
DxContext.cpp FileのShutdown()関数にFenceのReleaseを追加していました。
確認します。
既に追加されていました。
のでWarningが起きなかったんですね。
はい、これで実装は終わりです。
実装中にでた疑問で後で調べると述べた内容は来週調べる事にします。
今週のLötwig Fusel氏のTutorialの勉強はここまでとします。
8.2 「DirectX 12の魔導書」を勉強する
8.2.1 先週の復習をする
先週は「5.2 Vertex情報にUV情報を追加する」を実装しただけでした。
特に勉強し直す事も無かったです。
次の節を勉強します。
8.2.2「ShaderにUV情報を追加する」を勉強する
追加したUV情報を利用出来るように、Layout設定とShaderを書き換えるそうです。
Layour設定です。
以下のCodeを追加しています。
DXGI_FORMAT_R32G32_FLOATの部分だけ変更されています。
これはTextureはUVの2つのValueしかないため、Float2つ分のDataが必要だからです。
今度はShader Fileの実装を変更します。
BasicVSにFloat2 uv:TEXCOORDを追加します。
ここからが一寸複雑なんですが、この次にこのUVがきちんとPassされているかを確認するために、Vertex ShaderとPixel Shaderのやり取りを行うためのStructを生成するそうです。
うーん。
Vertex ShaderとPixel Shaderのやり取りを行うためのStructを生成するだけなら理解可能ですが、UVがPassされているかどうかを確認するためにVertex ShaderとPixel Shaderのやり取りを行うためのStructとはどういう事なんでしょうか。
続きを読んでいきます。
まずStructを作成します。
作成する場所ですが、Vertex ShaderとPixel Shaderの両方でこのStructは使用するため、新たにBasicShaderHeader.hlsliと言うFileを作成してそこに作成するそうです。
そしてVertex ShaderとPixel Shaderの両方でこのBasicShaderHeader.hlsliをIncludeする事で、この新しく作成したStructを両方のFileで使用出来るようにします。
はい。
まず#include “BasicShaderHeader.hlsli”をBasicVertexShader.hlslとBasicPixelShader.hlslに追加します。
Sample CodeではBasicType.hlsliになっていました。
この後、BasicShaderHeader.hlsli Fileを作成してそこにStructを作成し、そのStructをVertex ShaderとPixel Shader内で使用していくんですが、名称が教科書とSample Codeで違っているのでここではSample Codeのやり方に沿ってまとめる事にします。
BasicType.hlsliに以下の実装を追加します。
Vertex ShaderとPixel Shaderのやり取りを行うためのStructを作成しています。
作成したStructはBasicVertexShader.hlslとBasicPixelShader.hlsl内で使用します。
BasicVertexShader.hlslの実装は以下のように変化しました。
Chapter4のBasicVertexShader.hlslの実装は以下のようになっているので大分変化しています。
でもよく読むとTexture CoordinateのUV値が追加されただけか。
見た目だけですね。結構変わってるように見えるのは。
BasicPixelShader.hlslの実装です。
これだとRはUの値、GはVの値は同じですが、Bは0、そしてAlpha値も0になってしまう気がします。
Texture2DのSampleがどんな値を返すのか一寸調べてみます。
分かりました。
Copilotに質問したり参照にされているSiteを読んだりしたら、電撃が走ったかのように閃きました。
これはあるTextureのUVで指定された一点のRGBAの値を返しています。
だからReturn ValueはFloat4になります。RとGの値は0~1の間ですが、Bはどうなんでしょうか?
MainでどんなTextureをPassしているのか覚えていません。がその値がここにPassされるだけです。
それだけの話でした。
教科書ではこの後、Projectを実行しています。
UV値が赤と緑で表示される四角形が表示されていました。
これが表示されたらUVの値がPixel ShaderまでPassされている事になります。
ああ。
そう言う事か。
この節の最初に言っていた「このUVがきちんとPassされているかを確認するために、Vertex ShaderとPixel Shaderのやり取りを行うためのStructを生成する」の意味がやっと分かりました。
以上です。
9. まとめと感想
なし
10. 参照(Reference)
[1] Jeremy Howard. (2022, July 21). Practical Deep Learning for Coders: Lesson 1 [Video]. YouTube. https://www.youtube.com/watch?v=8SF_h3xF3cE
[2] NVIDIA Studio. (2022, January 8). Setting up a project in NVIDIA Omniverse Create Part 1: Getting started [Video]. YouTube. https://www.youtube.com/watch?v=j-JPMgcyfvI
[3] Function. (n.d.). Epic Dev. https://dev.epicgames.com/documentation/en-us/uefn/functions-in-verse
[4] Specifiers and Attributes. (n.d.). Epic Dev. https://dev.epicgames.com/documentation/en-us/uefn/specifiers-and-attributes-in-verse#effect%20specifiers