【お知らせ】更新について
お久しぶりです。最後にブログ書いたの70日前らしいです。。。
早速ですが、お知らせということでしばらく記事の更新ができそうにないです。
理由としては、就職活動のため面接やらで忙しいのと、改めてHLSLやC++などの基礎を勉強し直し中のため、何かを開発、作成することがなく更新することもないためです。
もっと早くこのお知らせを書くべきでしたね。。。
次回の更新は完全に未定です。
もしかしたら思いついたものを更新する可能性もありますし、内定のお知らせの可能性もあります。
というわけで、しばらくブログ更新はお休みになります。
気長にお待ちください。
UEのツールを作る【2】
前回(一か月前)の続きです、、、。
いろいろ詰まったりして思うように進まず遅れました。
だいぶムキになってやっていたので、途中の画像とかコードがあまりなく文字ばかりになってしまいますが、ご了承ください。
はじめに
前回は友人の効率改善のためにツールを作成し始めましたが、既存の機能では解決できそうにないのでプラグインを作成することにしました。
そのためにソースコードのダウンロードやc++の勉強をしたので、それらのまとめみたいな感じになると思います。
下記リンクでソースコード、zipファイルを確認できます。
※このブログ内で記載されているコードは一部となります。
結果
完成品の動作になります。
下記リンクで詳細が見れます。
https://www.vivivit.com/works/918410
制作過程
こちらを主に参考にしながら始めました。

「エディタツールバーボタン」で作成したらクリック後の動作を考えるだけで済みますが、初回かつ後学のためと思い、「空白」から制作を始めました。(コードは参考にした)
空っぽスタートなので呼び出しできるようにエディタを拡張していきます。
StartupModuleでモジュールロード時の処理、ShutdownModuleでアンロード時の処理を記述しました。
StartupModule
ShutdownModule
Smart Pointers in Unreal Engine | Unreal Engine 5.5 ドキュメンテーション | Epic Developer Community
<>の中はオブジェクトの型が入るみたいですね。
他にも、"*"や"&"は演算子として使用するだけでなくポインタに関係する際に出てくるんですね。
このあたりがなんとなく分かってくると、ある程度は何をしているのか感じるくらいにはなってきました。
後はこのなんとなくをより具体的にしていきながら、モジュールとメソッドに何があるのか調べて何を与えるとどうなるかをドキュメントを見てメモしていきました。
感想みたいなので脱線しましたが、ここからはコードのメモです。解説できるほど理解していないのでメモです。
AddMenuExtesionでメニューを登録して、LoadModuleCheckedでエディタ拡張に必要なモジュールをロード。
CreateRawはメソッドをコールバックとして登録。
AddExtenderで拡張メニュー(Extender)の登録。
ShutdownModuleはExtenderの削除を行うので事前に有効であるか確認している。
ここからOnWindowMenuExtensionとOnMainFrameLoadです。
OnMyToolMenuExtension
BeginSectionで新しいメニューセクションを開始。
AddMenuEntryでセクション内に新しいメニュー項目を追加。
FUIActionで選択された際に実行するアクションを指定。これでOnMyToolMenuを呼び出し。
OnMainFrameLoad
新しいウィンドウじゃないかつ、InRootWindowが有効なポインタであるか。
初期化を行っている(多分)。
ガベージコレクションで適切に管理をしないとクラッシュの原因になりました。
なのでUPROPERTYマクロとTWeakObjectPtrを使用しています。
ガベージコレクション:メモリ上の不要なデータを自動的に削除する仕組み
[UE4] GC(ガベージコレクション)の基本 #UE4 - Qiita
UEに存在する機能で、便利な仕組みっぽいです。内部的なデータの流れをすべて理解していないので、詳しくはわかっていません。勉強中です…。
ウィンドウが閉じたときに、MyWindowをリセットしてメモリリークなどを防ぐ。
ウィジェットの作成。
SVerticalBoxやSHorizontalBox、STextBlockなどいろいろある。
スロットの追加
親元の箱みたいなイメージで、ここにPaddingなどで見た目の調整を行う。
だいたいこんな感じで作ってきました。
ここからは考え方とか参考サイトなど、いろいろのまとめです。
ーーーーーーーーーーーーー
UIのこと
マテリアルを指定>テクスチャを入力>グラフ内にノードを作成。という流れを想定。
処理を考えるにも、まずは入力ができないと始まらないので、UIの作成から。

編集/エディタの環境設定/未分類からデベロッパーツール/UIの拡張点の表示を有効にするとUIを作成する際に助かります。
あとはWidgetReflerctorとUEのソースコード、後は公式ドキュメントを参考にしました。
他にも、Starship Galleryも便利です。

SlateUIはc++で書いてる感じがしないくらい直感的に書けていいですね。
でもネストばっかでちょっとHTML感があるのは見づらくてややこしいなって感じました。
UIで苦戦したのは、サムネ付きのプルダウンの選択でした。

↑この選択するやつ
こんな感じで実装しています。
SObjectPropertyEntryBoxが大事で、そこからはフィルタ、パス、クリアボタンなどの設定です。
参考↓
SObjectPropertyEntryBoxでActorアセットを選択する話 - crssnkyの。
これで選択できるようになってちょっとUEのツールって感じがしてテンション上がりました。
ここからボタンを二つ追加します。
一つは確定させてウィンドウを閉じるボタンで、もう一つは連続して設定するためのボタンです。
次に中身を考えて作っていきます。
内部の処理を行うMakeNode.cppは次のブログで書きます。
詰まったとことか
まず、いきなり5.4でc++のプロジェクトが作成できない状態になりました。
エラー画面のスクショやログが残っていないのですが、試行錯誤の記録を残しておきます。
解決策1
UEの再インストール
理由
5.5で作成できたので環境が問題ではないと判断したため。
結果
変わらず作成できない。
解決策2
Visual Studio Installerでとにかくコンポーネントをインストール。
理由
5.4用のコンポーネントが不足していると考えたため。
結果
作成できた。
まとめ
おそらく、最新のコンポーネントにエンジンが対応していない?と思われるのでなるべく多くのバージョンのMSVCなどをインストールしておけば再発防止できるかも。
次にビルド環境構築で詰まりました。
これが非常に気づきにくくて時間がかかりました。が、解決法はシンプルでVisual Studio Build Toolをインストールしていなかったからでした。
【UE5】VSCodeでプロジェクトのビルドを行う。 - アルゴンUE4/UE5&アプリ開発日記
こちらのサイトを見つけてようやく気付きました。
UEのライブコーディングでエラーが出ていないのに結果が反映されず、ずっと自分のコードを疑い続けて一歩も進まない状態が続きました。
反省:環境構築や初心者向けの基本的な情報を流し見せず、しっかり読み込む。
あとがき
またまた更新が遅くなりました。
上記の詰まったところで、進まなかったり、できな過ぎて心折れてたりして制作自体に時間がかかってしまい、更新できませんでした…。
一区切りつくとこまですすんだらこまめにブログを書くべきか…。
どうしてもある程度の成果がでないと書く内容が薄くなるんですよね~。
ちょっといろいろ検討してみます。
ただ、また次も遅くなりそうです。(ES書いたり就活でやることががが)
今回はメインの実行ファイルまでの内容で、次は処理を担うコードの解説?メモ?になります。
二重括弧を勝手に脚注にするな()
その他参考サイト様's
Unreal Engine Plugins | プラグイン制作 | UE5攻略リンク
一週間で身につくC++言語の基本|トップページ~C++言語の初心者でも、簡単にプログラミングが気軽に学習できるサイトです。
UnrealC++でレベルエディタに独自メニューを追加する #UE4 - Qiita
【UE4】エディタ拡張(インプットボックス編) – 株式会社ロジカルビート
独自のタブをエディタのメニューから呼び出す方法 #UE4 - Qiita
【UE4:エディタ拡張】左右にレイアウトを揃える #UnrealEngine4 - Qiita
[UE4]プラグインをパッケージ化しよう!|株式会社ヒストリア
【UE5.4】 プラグインの単体ビルドに関するあれこれ | Alche, Inc
他にも公式ドキュメントのページなどありましたが、あまりにも多すぎるため一部となります。
UEのツールを作る【1】
友人の作業効率化のためのツールをつくることにしたので、その記録です。
今回作るツールはマテリアルの自動設定を目標にしています。
はじめに
友人の困りごと
- マテリアルをいちいちベースカラーやノーマルマップなどの作成が面倒
- srgbとリニアの設定が面倒
このあたりを解決できるようにツールを作りたいと思います。
環境設定
UEのバージョンは5.5.3です。
作成する前に、マテリアルインスタンスを使えばマテリアルグラフの画面でノードを作成したりする手間が省けるのではと思い聞きましたが、機能をよくわかっていないとのことでした。なのでマテリアルインスタンスを作成するツールと、パラメータの設定を簡単に行えるツールの二つに分けて考えたいと思います。
UEのツール作成はエディタユーティリティウィジェット(EUW)やエディタユーティリティブループリント(EUB)があるのでそれらで作成できないか試してみます。
その一
こちらの公式を参考にやってみました。
こちらのブループリントは対象のスタティックメッシュに特定のマテリアルを割り当てるものです。

このキューブに対して

コンテンツドロワー上で右クリック→スクリプト化されたアセットアクションから実行すると

マテリアルが変更できました。
しかし、これを複数のマテリアルを持つメッシュに対して行うと、


オブジェクトの持つすべてのマテリアルが変更されてしまいました。
このままではブループリントを編集しないと割り当てるマテリアルを変更できないかつ、メッシュの持つすべてのマテリアルを変更してしまいます。
したいことはマテリアルグラフに指定ノードの作成なので違いますね。これはこれで何か別のツールを作成する際に使えそうですが。
というわけで他の情報を探してみます。
その二
こちらの記事を見つけたのでやってみます。
どうやらマテリアルインスタンスダイナミック(MID)というのを利用するようです。
こちらが公式ドキュメントです。
ぱっと見はエディタ用の機能ではなさそうですが、一度作成してみます。

こちらが参照されるパラメータをもつマテリアルです。

これでBaseColorとNormalのパラメーターの変更ができました。
しかしこの方法では事前にインスタンス化するマテリアルの設定をしないといけない、さらにMIDで作成されたマテリアルのパラメータのプレビューや後から割り当ての確認などもできませんでした。
やはりランタイム用の機能であり、エディタ上で使うものではありませんでした。
おわりに
他に調べても、ブループリントでマテリアルグラフのノードを作成することはできなそうです。
となると、c++で直接コードを記入して作っていくしかなさそうです。
次回はc++でコードを書いていく予定です。(現在勉強中)
結局更新遅くなりましたね。。。
あと誰かblueprintUEの上手な張り方知りませんか()
SRP作成(3)
三回目(実質二回目)引き続きやっていきます。
前回はこちら
はじめに
こちらのサイト様を参考にすすめています。
今回はこのドローコールのページです。
シェーダ
描画にはCPUがGPUに描画指示をしなければならない。どのように描画するかはGPUの命令セットであるシェーダーによって定義される。
シェーダーはメッシュのほかにオブジェクトの変換行列やマテリアルプロパティなどの追加情報を必要とする。
コンパイルできる最小のシェーダー
Passブロックに記述する。
この間にHLSLを記述する。
メッシュを描画するために、GPUはすべての三角形をラスタライズしピクセルデータに変換する必要がある。
頂点座標を3D空間から2D可視化空間に変換し、その結果の三角形で該当するピクセルを塗りつぶす。
前者は頂点シェーダ。
後者はフラグメントシェーダ。
各シェーダを識別するのはプラグマ指示文で行われる。
VertexかFragmentのどちらかと関連する名前が続く。
HLSLにはクラスの概念がない。コードブロックのローカルスコープ以外に、単一のグローバルスコープしかない。なのですべてのものがどこからでもアクセス可能。
ファイルをインクルードするとファイルの内容すべてをインクルードディレクティブの位置に挿入する。
同じファイルを複数回インクルードするとコードが重複するので、インクルードガードを追加することで、コンパイラエラーを防ぐ。
defineディレクティブで任意の識別子を定義できる。
ifndefディレクティブで識別子の定義を確認する。
もしマクロが定義済みならコンパイルされないようにスキップする必要がある。
ので、#endifを追加してスコープを終了させる。
フラグメントシェーダーが色を返す。
positionとtexture座標のみにfloatを使い、それ以外にはhalfを使ったほうがいい?(モバイル用)
モバイルをターゲットにしていない場合、大抵のGPUはfloatを使うのでhalfでも精度は問題ない。
セマンティクスでパラメータの使用目的の指定。
positionOS オブジェクト空間(Object Space)
位置座標はx,y,zと常に1に設定されるwで構成される。wは位置と方向の区別に使う。これにより同じ変換行列(マトリクス)を使って位置と方向を正しく変換できる。
w = 1 位置(点)
w = 0 方向(ベクトル)
位置は移動回転スケールの影響を受ける。
ベクトルは移動の影響を受けず、回転スケールのみの影響を受ける。
頂点データには位置以外も含まれることがあるので、セマンティクスを追加する。
空間変換には行列が必要。これは何かが描画されるときにGPUに送られる。
行列は常に同じで、Unityが提供する標準入力がある。
UnityInput.hlsl
インクルードガード内で記入する。
オブジェクト座標をワールド座標に変換するための行列。
c#ではフィールド変数と呼ばれる。
シェーダではユニフォーム変数と呼ぶ。(uniform = 一定)
描画中は値が変わらない変数で、GPUが一回の描画ごとに一回だけ設定する。全ての頂点・ピクセルで同じ値を使う。
Common.hlsl
mul : 行列とベクトルの掛け算(マトリクス変換)
計算のためにfloat3のpositionOSのwに1を入れてfloat4にする。
ローカル座標をワールド座標に変換して.xyzで出力をfloat3にする。
関数の上にCommon.hlslをインクルードする。(相対パス)
TransformObjectToWorldの入出力はfloat3なので、positionOSのxyzのみを渡して、ワールド座標に変換。positionWSに代入。
ワールドスペースからクリップスペースに変換する必要がある。
→プロジェクション行列
クリップスペース:カメラの視界に収まる形に変換した座標。
View-Projection行列
クリップ座標:HClip (Homogeneous Clip Space)
WSからHClipに変換。
頂点シェーダで使用する。
上記の変換用関数二つはCore RP Pipelineパッケージに含まれている。
変換関係ファイル。
unity_ObjectToWorldの存在を仮定していない。が、関連する行列がマクロによってUNITY_MATRIX_Mとして定義されている。
これでUNITY_MATRIX_Mをunity_ObjectToWorldで置き換える。
他の行列も置き換える。
Unity2022はこれら三つの行列が必要。
UnityInputにも追加する。
これも追加する必要がある。
real4と定義されたベクトル。プラットフォームによってfloat4かhalf4になる。
ここに基本的なマクロが入っている。
先頭にアンダースコアをつけるのはマテリアル・プロパティを表す標準的な方法。
インクルード下、Vertex関数前で記述。
プロパティブロックで記述することでマテリアルとリンクする。
バッチング
ドローコールを実行する度にCPUとGPUの通信が必要になる。
バッチング(バッチ処理):ドローコールをまとめる処理。
CPUとGPU間の通信に費やす時間を短縮する。
SRPBacher:インスペクターで有効か確認できる。ドローコールを減らすのではなく無駄のないようにする。マテリアルプロパティをGPUにキャッシュして、ドローコールの度に送信する必要がないようにする。シェーダが一様なデータの厳密な構造を順守している場合のみ機能する。
すべてのマテリアルプロパティは、グローバルレベルではなく、具体的なメモリバッファ内で定義する必要がある。
cBufferブロックでラップすることで行う。構造体宣言のように機能するが、セミコロンで終了する必要がある。
特定の定数メモリバッファで分離するが、グローバルレベルでアクセスは可能なまま。
cBufferはすべてのプラットフォームでサポートされているわけではないので、CoreRPLibraryに含まれるCBUFFERマクロを使う。バッファ名を引数として取る。
これらもバッファに含む必要がある。
これでシェーダのインスペクターにSRP Compatibleが表示される。

あくまでシェーダに互換性があるだけで、SRPBacherを有効化するにはGraphicsSettings.useScriptableRenderPipelineBatchingをtrueにする必要がある。
CustomRenderPipelineに追加して、パイプラインインスタンスが作成されるときに実行する。
マテリアルが増えても同じシェーダならバッチは一回になる。マテリアルの中身ではなく同じシェーダを使っているかで判断している。唯一の制限はマテリアルごとにメモリ配置が同じでなければならない。(単一カラーのプロパティのみを含むなど)
カスタムコンポーネントを作成することでオブジェクトごとに色を設定できる。
→色ごとにマテリアルを作成する必要がなくなる。
一つのゲームオブジェクトに2つ以上アタッチできないようにする。
_BaseColorプロパティにアクセス。
インスペクターで編集可能にする。
オブジェクトごとにマテリアルのプロパティを変更するためのクラス。
インスペクターの値を変更したときに自動で実行。
Rendererコンポーネントを取得。オブジェクトのマテリアルにblockを適用。
エディタ上のみでなく実行時にも適用するようにする。
GPUインスタンシング
同じメッシュを持つ複数のオブジェクトに対して一度に単一のドローコールを発行することで動作する。
CPU オブジェクトごとのトランスフォーメーションとマテリアルのプロパティを配列に入れてGPUに送信
GPU すべてのエントリを反復処理し、提供された順番にレンダリングする。
GPUインスタンシングを有効にするプリプロセッサディレクティブ。
GPUインスタンシングをサポートするためにインクルードする必要があるファイル。
構造体でセマンティクスをラップする。
インスタンシング用のIDを定義するマクロ。
入力からインデックスを取り出し、他のインスタンス化マクロが依存するグローバルなstatic変数に格納する。
CBUFFER_START,ENDから置き換えることで、_BaseColorを配列参照に置き換える。
GPUインスタンシング対応のプロパティ。
インスタンスインデックスもPassFragmentで利用できるようにする必要がある。
PassVertexに位置とインデックスの両方を出力させて、それをコピーするために構造体を使う。
inputからoutputへインスタンスIDをコピー
オブジェクトのインスタンスIDを取得し、マテリアルごとのバッファから_BaseColorを取得して返す。
GPUインスタンシングは同じマテリアルを共有するオブジェクトに対してのみ機能する。
バッチサイズには、データ量に基づく制限がある。この制限を超えると複数のバッチになる。
ダイナミックバッチング
同じマテリアルを共有する複数の小さなメッシュを一つの大きなメッシュにまとめて代わりに描画する古い手法。小さなメッシュにしか使えない。
GPUインスタンシングを無効にし、enableDynamicBatchingを有効化。
同じくSRPバッチャーも無効。
ダイナミックバッチングは複数のメッシュを一つのメッシュにするため描画の順序が変わる。
透明
透明描画はソースとディスティネーションのブレンドモードを設定することでできる。
ソース:今描画されるもの。
ディスティネーション:先に描画されたもの。その結果が最終的に到達する場所。
ソース1は完全に追加。ディスティネーション0は無視される。
標準的な透明度のソースブレンドモードはSrcAlphaで、レンダリングカラーのRGBにアルファが乗算されることを意味する。つまり、アルファ値が低いほど弱くなる。
ディスティネーションは逆のOneMinusSrcAlphaに設定されて合計ウェイトが1になる。
Blendステートメントのあとに2つのモードを続けることでPassブロックで定義できる。
シェーダーのプロパティを使いたいので[]に入れてアクセス。
透明レンダリングは普通、depthバッファに書き込まない。
ZWriteステートメントでdepthを書き込むかどうかを制御できる。
Textureプロパティは、変なエラーを防ぐために空のコードブロックで終わらせる必要がある。
テクスチャはGPUメモリにアップロードする必要がある。これはUnityがやってくれる。
シェーダには関連するテクスチャへのハンドルが必要で、TEXTURE2Dマクロを引数として名前を使う。また、テクスチャのサンプラーステートを定義する必要がある。これはテクスチャのラップモードとフィルタモードを考慮してどのようにサンプリングするかを制御する。SAMPLERマクロで行う。
テクスチャとサンプラーステートはシェーダリソース。インスタンスごとに提供はできず、グローバルスコープで宣言する必要がある。UnlitPass.hlslのシェーダプロパティの前に行う。
Unityはテクスチャのタイリングとオフセットもテクスチャプロパティと同じ名前だが、スケールとtranslationのようなものを表す_STが付加されたfloat4を介して利用できるようにしている。このプロパティはUnityPerMaterialバッファの一部である必要があるためインスタンスごとに設定可能。
テクスチャをサンプリングするには、頂点属性の一部であるテクスチャ座標が必要。
具体的には座標の最初のペアが必要。AttributesにTEXCOORD0の意味を持つfloat2フィールドを追加することで追加することで行う。これはベースマップのためのもの。
テクスチャがサンプリングされるので、座標をfragment関数に渡す。
特別な意味を追加する必要はなく、GPUが特別な注意を払う必要のないデータを渡すだけ。そこでVAR_BASE_UVを使用する。
UnlitPassVertexで座標をコピーするときに、_BaseMap_STに保存されているスケールとオフセットも適用できる。この方法ではフラグメント単位ではなく頂点単位で行う。
スケールはXY、オフセットはZWに保存されて、swizzle propertiesでアクセスできる。
UV座標がUnlitPassFragmentで利用可能になり、三角形全体で補間される。
テクスチャ、サンプラーの状態、座標を引数として、SAMPLE_TEXTURE2Dマクロを使用して、テクスチャをサンプリングする。
アルファクリッピング
レンダリングされるフラグメントの一部を破棄することで、アルファクリッピングをすることができる。カットオフのしきい値を定義することで、下回るアルファ値を持つフラグメントを破棄し、それ以外を保持できる。
UnlitPassFragmentのclip関数を呼び出すことでフラグメントを破棄することができる。
渡された値がゼロ以下であればフラグメントは中断され、破棄される。そこで、aまたはwプロパティからアクセスできる最終的なアルファ値からカットオフしきい値を引いた値を渡す。
マテリアルは透明ブレンディングかアルファクリッピングのどちらかを使うが、両方を同時に使うことはない。アルファクリッピングマテリアルは破棄されたフラグメント以外は完全に不透明でdepthバッファに書き込まれる。これはAlphaTestレンダーキューを使用するため、不透明オブジェクトのあとにレンダリングされる。これは、三角形がその後ろにあるものを完全にカバーすることを想定できなくなるため、フラグメントを破棄するとGPU最適化が不可能になることがあるため。なので、最適化するためにアルファクリッピングをトグルによって切り替えられるようにしなければならない。
トグルを有効にしたときに異なるバージョンのシェーダがコンパイルされるようにUnityに指示する。
#ifdef_CLIPPINGも使うことができるが、好みで機能は変わらない。
カットオフはUnityPerMaterialバッファの一部なのでインスタンスごとに設定できる。
あとがき
ポートフォリオの作成や、学校のイベントが重なって更新が遅くなりました。
グラフィックスエンジニア寄りのTAを募集してる会社ってなかなかないので大変です。。。
次の更新内容はツールになると思います。UEのマテリアルに関する便利ツール予定。
週一の更新を安定してできるようになりたいですねぇー。
参考
SRP作成(2)
前回SRPのブログを書いてから約二か月空いてしまいましたが、ようやく第二回です。
書き方などを探りながらなので安定していないかもしれません。
はじめに
https://shaurasan.hatenablog.com/entry/2024/11/28/153258
上記リンクが前回ですが、時間が空いてしまったので無いものとしてください。
なので、今回が実質第一回です。
下記のサイトの内容に沿って作成していきます。
非常にわかりやすくて、助かります。
つくるよ
メモのような感じに書いていきます。
CameraRenderer.cs
ScriptableRenderContext カスタムレンダリングパイプラインが使用する状態と描画コマンドを定義
Camera カメラ情報
CustomRenderPipeline.cs
異なる型に対応するメソッドの定義
すべてのカメラをループでレンダリング
CameraRenderer.cs
スカイボックスの描画
コマンドをGPUに送信
無いと描画がされない。
呼び出すとScriptableRenderContextの状態がリセットされる。
カメラのプロパティ(ビュー行列、プロジェクション行列、カメラパラメータなど)をセットする。
コマンドバッファの作成
コマンドバッファは描画コマンドを一時記録して、必要なタイミングでまとめて実行することができる。
Profilerの計測開始、終了の指示。
負荷の確認用。
コマンドバッファの実行とクリア。
適切なタイミングで実行する必要がある。
現在のレンダーターゲットのクリア。
深度バッファ、色バッファ、クリアカラーの三つの引数。
前フレームの描画内容をリセットする。
カメラプロパティを先に設定してからレンダーターゲットをクリアすると、より高速になる。
全体に透明なシェーダーでクリアする方法からGPUハードウェアクリアに代わる。
深度バッファとステンシルバッファは同じメモリを共有している。
SRPのカリング操作を設定するパラメータ
カリングパラメータの取得
取得したらtrue できなかったらfalse。
outはメソッドに渡されるときに初期化されていなくていい。
メソッド内で値を設定する必要がある。
falseの場合に処理をスキップする。
カリング操作の結果を含む構造体。
refは呼び出し元が初期化した値を渡す必要がある。
入力と出力の両方に使える。
大きな構造体をメソッドに渡すときに使うとコピーを避けることができる。
構造体はオブジェクトのようにメモリを割り当てないので効率的。
context.DrawRenderersの設定。
オブジェクトの描画設定と、描画オブジェクトのフィルタリングの設定。
レンダリングコンテキストに基づいて、オブジェクトを描画するメソッド。
シェーダのタグを識別するためのクラス。
static 変数がクラス全体で共有される。
オブジェクトをどの順番で描画するかを設定する。
描画設定。第一引数は使用シェーダ。第二引数は描画順。
レンダリングキューを対象にフィルタリング。
描画順序の指定。CommonOpaqueは不透明オブジェクト。
不透明だけを先に描画する。
スカイボックスの描画後に描画順を変更し、drawSettingに適用。
fliteringSettingをtransparentにすることで、透明オブジェクトのみを描画対象にする。


一回目のDrawで不透明描画。
スカイボックスを挟んでから二回目のDraw
ここでは透明のみ描画。
レガシーシェーダを特定するShaderPassId
legacyShaderTagIdの配列をループでdrawingSettingに登録
SetShaderPassName 第一引数:シェーダーパス 第二引数:シェーダータグ
FlieteringSettings.defaultValue:すべてのオブジェクトを描画対象にする。
これでレガシーシェーダーが描画されるが、シェーダープロパティを設定していないので正しく描画されない。
エラーマテリアルの作成
クラスや構造体を複数の部分に分割して、別々のファイルに格納する方法。
コードを整理するためにつかう
エディター内のみで動作するコード
ビルドすると無視されるようになる。
その他
ギズモを描画すべきか判定
ギズモの描画タイミングを指定
ポストプロセス前と後。
Sceneビュー専用のカメラを指定
指定されたカメラがSceneビューでワールドジオメトリを描画できるようにする。(3D モデルやメッシュ)
UIを明示的に追加
この処理を含むメソッドはカリングの実行前に行う。
デフォルトメインカメラのDepthは-1
カメラはDepthが深い順にレンダリングされる。
カメラの名前とバッファの名前を合わせる。
値の取得と設定をできるようにする。
camera.nameの値をSampleNameに代入。
SampleNameの値をbuffer.nameに代入。
const 定数として設定
SampleNameを定数bufferNameとして扱う。
プロファイラーで表示する名前を指定。
これでBeginSampleとEndSampleのカウントが一致しなければならない。という警告が出なくなる。
エディタのみでメモリを確保していることを明確にする。
UnityEngine.ProfilingからProfiler.BeginSampleとEndSampleを呼び出す。
名前を渡すのはBeginのみ。
"Editor Only":測定範囲の名前。
カメラは特定のレイヤー上のものだけを見るように設定できる。
カリングマスクを調整することで行う。(インスペクター)
カメラのクリアフラグを調整することで、両方のカメラの結果を組み合わせることができる。
カメラが新しいフレームを描画する際に、前のフレームをどのようにクリアするか指定するプロパティ。
Skybox,Color.Depth,Nothingの1~4の値がある。
3,4 Depth,Nothingの場合カラーバッファをクリアしない。
Unity2022はレンダーターゲットに数値でない値や無限大の値が含まれていると、ブレンドのアーティファクトが発生する可能性がある。また、フレームデバッガがランダムなデータを表示する可能性がある。
クリアフラグがcolorならクリア。
カラーバッファのクリアカラーをcolorなら背景色。それ以外なら透明色。
.linearはリニアカラースペースでレンダリングしているため、色をリニアスペースに変換する必要がある。
最初に描画されるメインカメラのクリアフラグはSkyboxまたはColorに設定する必要がある。フレームデバッガを有効にしても常にクリアされるわけではない。
セカンダリカメラのクリアフラグによって二つのカメラの描画結果がどうやって組み合わさるか決まる。
スカイボックス、カラーなら完全に置き換える。
depthなら背景が変化しない。(スカイボックスが描画されない。)
nothingならdepthが保持されるので、オブジェクトがそのまま表示される。透明オブジェクトは前景に重なって表示される。
複数のカメラで1フレームをレンダリングすると、カリングなどの処理が複数回発生するので、一つの視点ごとに一つのカメラを使用するほうが、効率的。
今回はここまでです。
あとがき
参考サイト様の一ページ分でした。
めっちゃ更新遅いので、ペース上げられるように頑張ります。
しかし、参照するドキュメントが多い、、、。
次はできれば来週までには更新したいですね~。
UnityDocunentations
Unity - Scripting API: ScriptableRenderContext
Unity - Scripting API: ScriptableCullingParameters
Unity - Scripting API: CullingResults
Unity - Scripting API: DrawingSettings
Unity - Scripting API: FilteringSettings
Unity - Scripting API: ShaderTagId
Unity - Scripting API: SortingSettings
Unity - Scripting API: RenderQueueRange
Unity - Scripting API: SortingCriteria
Unity - Scripting API: Material
Unity - Scripting API: Handles
Unity - Scripting API: GizmoSubset
Unity - Scripting API: SceneView
Unity - Scripting API: CommandBuffer
Unity - Scripting API: CameraType
Unreal Engineのリモート実行とPythonを試してみた
UEFEST 2024 TOKYOの下記の講演内容を実践した話です。
レンダリングパイプライン作成の息抜き程度に試してみました。
mayaなどのdccツールからunreal project内のpythonスクリプトをリモートで実行できるようです。
事前準備
まずはプラグインウィンドウから下のプラグインが有効化されているか確認します。

次にエディタの環境設定からコンテンツブラウザ統合を有効化します。

これでエディタ内でpythonスクリプトが表示されるようになり、実行も可能になりました。
最後に今回の本命であるリモート実行を有効にします。
プロジェクト設定からリモート実行を有効にするにチェックをつけます。

これで事前準備は完了です。
他にも、デベロッパーモードなども有効にすると関数の型情報なども生成されるようです。
リモート実行の検証
事前準備が済んだので早速dccツールからUE内のスクリプトを実行してみたいと思います。
シンプルなログを表示するスクリプトをUE側に用意します。
sample_call.py
次にこのスクリプトをリモート実行するスクリプトを用意します。
call_dcc.py
このスクリプトをmayaのスクリプトエディタから実行します。

無事にログが出力されました。
(Hello World にならなかった…)
エラーでmaya側からできなかったりしたときは、UEのインストール先からバージョン/Engine/Plugin/Experiminal/PythonScriptPlugin/Content/Pythonをsys.path.appendで追加するとできたりしました。
リモート実行が確認できたので、オブジェクトのエクスポートもやってみます。
試したのはスタティックメッシュのみです。
UE側のコード
Py_Import_STMesh.py
dcc側のコード
autoExport.py
これで一応自動エクスポートができました。
改造
現状の気になるところ
- エクスポートは手動
- 参照フォルダが固定
- 読み込み設定が固定
- フォルダ内のすべてを毎回参照する
これらを解消してすこしでも使いやすいものにします。
理想形は
- mayaで選択したオブジェクトをエクスポート+UEにインポート
- インポート先の指定
- インポート設定を変更できるように
以上三点とします。
ではこれらを実現するコードを書いていきます。
まずはUIを想定します。
スタティックメッシュのみを想定して、インポート設定を考えます。
必要そうなのは、
- 上書き保存するか
- マテリアルも書き出すか
- テクスチャも書き出すか
これらはオンオフのみでいいのでチェックボックスを三つ。
次にインポート先フォルダを指定するためにテキストボックスとエクスプローラーを開くボタンが必要そうです。
以上の要素を含むウィンドウをラフに設計します。

かなり字が汚いですがラフということで
だいたい形が想定できたのでコードを書いていきます。
これをmayaのスクリプトエディタで実行します。

いろいろ気になりますが使う分には問題なさそうなのでokとします。
次は処理を書いていきます。
一気にUIとまとめましたが、これでUE側に用意した'Py_ImportSample.py'をリモート実行されて、選択したオブジェクトをfbxとしてmayaプロジェクト内のdataフォルダに書き出し、それをUE内の指定したフォルダにインポートするようになりました。
Py_ImportSample.pyの中身は以下の通りです。
今回のコードはgitで公開しています。
https://github.com/shaurasan/MayaToUE
参考
mayaPythonヘルプ
http://me.autodesk.jp/wam/maya/docs/Maya2010/CommandsPython/cat_Windows.html
Layoutmanagerのこと
window自体の作成のこと
あとがき
Pythonの勉強とmayaスクリプトのいい勉強になりました。
ただ、これの作成に夢中になりちょっとc#やHLSLが抜けてそうでやばいです。
定期的にどの言語も使わないと忘れちゃいますね。
SRP作成(1)
SRPとは
SRP(Scriptable Render Pipeline)
Unityのc#を使用してレンダリングの制御ができる機能です。
スクリプタブルレンダーパイプラインの基礎 - Unity マニュアル
URPとHDRPもSRPの一種のようです。
カスタムSRPを利用するには、描画用のインスタンスとレンダリングパイプラインアセットを作成し、Project Settingで適用する必要があるようです。
こちらを参考にインスタンスとレンダリングパイプラインアセットを作成します。
SRP作成
こちらがレンダリングパイプラインアセットの中身になるコードです。
現在は何も処理を入れていません。このままでは使えないのでいったんアセット化できるようにします。
これでProjectWindowから右クリック/作成/レンダリングにExampleRenderPipelineAssetが表示されるようになり、アセットの作成が可能になりました。
一度このまま適用してみます。
ProjectSettingから品質の中にあるレンダーパイプラインアセットを作成したものに変更します。

シーンビューに何も表示されなくなったら成功です。

オブジェクトも一切描画されませんし、スカイボックスなども表示されなくなりました。
中身を書いていないので正しい状態です。
ここから処理を書いて描画できるようにしていきます。
中身の作成
ここからは以下を参考に進みます。
カメラは一つずつ呼び出して、処理を行いそれをforeachでカメラの数だけ実行していますね。
カリングやフィルタリングのあたりは現状よくわからないです。
ScriptableRenderContextは以下の公式ドキュメントを参照して記述できそうですね。
Unity - Scripting API: ScriptableRenderContext
いったんこれでUnity側を確認してみます。

グリッドが表示されるようになりました。
オブジェクトは専用のシェーダーを記述する必要があるので、次回にやります。
最後にスカイボックスの描画までやって今回は終わりにします。
ここからはこちらの記事を参考に進みます。
スカイボックスはScriptableRenderContextのDrawSkyboxで描画できるようなので、書き加えていきます。
DrawSkyboxとSetUpCameraPropertiesを書き加えました。
SetUpCameraPropertiesはシーンを正しくレンダリングするため、カメラのプロパティをコンテキストに適用するために必要らしいです。
これでUnityを確認するとSkyBoxが描画されているはず。

いけました。
今回はここまでにします。
次はもう少し詳しいことやシェーダーを作成してオブジェクトを描画などを目標に進みます。
c#とレンダリングあたりのことを同時に勉強しているのでなかなか大変ですね。
あとがき
CEDEC+KYUSYUに行ったりしていたので更新が遅くなりました。。。
UEFESTとかにも目を通していて、いろいろやりたいことが蓄積中です。
もう少し形になったものができたらgithubなんかも活用していきたいです。