AssetBundleとAddressables

環境

Unity2021.2.19f1

本題の前に

アセットと言われるとどうしても、様々な物が含まれている物を1つにまとめた奴、
っていうイメージが自分にはついてしまっている
実際には画像や音声、テキスト、モデル、プラグインとかのゲームを構成するファイルの総称っぽい
参考
サポートするアセットタイプ - Unity マニュアル
で、asset 自体は資産・財産・有用な物っていう日本語になるみたいだけど、ちょっと違うよなぁってなって
良い言い換えないかなぁってなったんだけど、
芸術・工芸はどっちかというと物質な部分が多いし、構成品は音声やモデルに想像が行きにくい、
創作品だと総称というよりかは完成形だし・・・
源成物(造語)だと壮大すぎる、作成物はそのまま・・・いやそのままならいいんじゃないか・・・?
品だと完成形っぽいけど物だと単体っぽい感じ(本来の違いは知らん、品と物の違いを調べようとしたら汚染がひどい)

ので、ここではアセットを作成物と呼称して押し付けていく

ついでにbundleは束とか塊とかまとめるとかそんな意味らしい
つまり AssetBundle は 作成物まとめ ということになる
おっ、一気に俗臭くなったな!

AssetBundle

上に書いたが「AssetBundle」を「作成物まとめ」として解釈する

で、
アセットバンドルのワークフロー - Unity マニュアル
に書いてある、「バリアント」を
AssetBundleのVariantsを完全に理解する - Qiita
を元に「版」(決定 版、HD 版とかの版)として解釈すると
「3. Inspector ウィンドウの下部に、アセットバンドルとバリアントを割り当てるセクションがあります。左側のドロップダウンでアセットバンドルを割り当て、右側のドロップダウンでバリアントを割り当てます。」

「3. Inspector ウィンドウの下部に、まとめ先と何版かを割り当てる場所があります。左側でまとめ先、右側で版を割り当てます。」
と解釈できる、簡単だな!

で、この作成物まとめを作るには
アセットバンドルのワークフロー - Unity マニュアル
1. 各作成物のまとめ先と何版かを割り当てる
2. UnityEditor内で動くプログラムで各作成物まとめを作る(アセットバンドルをビルドする)
3. 作った各作成物まとめの中から色んな作成物をロードする
という流れらしい
面倒だな!!特に1.!!!

この作成物まとめを作る利点は・・・
圧縮とメモリの節約・・・ってことらしい・・・たぶん
あとはResourcesの起動遅い問題が解決する・・・とは書いてないけどたぶん解決する・・・

Addressables

この辺とか
Addressable Assets Systemをちゃんと導入するための技術検証まとめ – てっくぼっと!
この辺とか
Addressable Assets Systemを完全に理解する - Qiita
から見るに、「作成物」と「付けた名前」の間にある実行環境や版毎に違うパスや依存関係をうまく処理してくれる何か・・・
AssetBundleとは別物・・・っぽい・・・

とりあえず
初めに | Addressables | 1.20.5
に書いてある通りに
Addressablesを入れる、グループを作る、作成物のInspectorの一番上にあるAddressableを有効にする、
サンプルコード通りに入れる(addressはAddressablesGroupsウィンドウの左側の名前(AddressableName)にした)
で、前回のと同じ条件でロード方法を次のようにした

async void loadA() {
        var handle = Addressables.LoadAssetAsync<Sprite>("Assets/Gaibu/128.png");
        var sp = await handle.Task;
        spriteStack.Push(sp);
        Addressables.Release(handle);
}

非同期処理?知らねぇなぁ!!!!

で、これの平均時間は0.039秒だった
遅い時は0.06秒ぐらい、早い時は0.027
秒ぐらいだった
Resources.Loadよりは遅いがその他手法に比べれば早いって感じ
まぁ依存関係とか割り当てとかの処理で遅くなってるんだろうなぁって感じ

えっ、非同期にすればもっと早いって?
貴様には消えてもらう

とりあえずAssetBandleとかいう作成物まとめは気にしない方向で
Addressablesを用いる前提でファイル更新時の置き換え自動化処理にいってみる


nsan.hatenablog.com

画像表示用のスプライトのロード速度比較

概要

unityで画像を毎回ドラッグでインポートするのめんどくさい

なんかこう自動化するか

しかし、Resourcesからの読み込みとか、直接ファイルからの読み込みでロードの速度差あるんかな

環境

Windows 10
Unity 2021.2.19f1
Intel core i5-11400
NVIDIA GTX1060

手法

Editor上のPlayボタンにより計測を開始する
各ロード方法により同じ画像を10000枚だけ読み込ませる
読み込みを100周行い、その方法による平均ロード時間を出す

方法1. 毎回Resources.Loadする
方法2. 初回だけResources.Loadして、次からは最初に作ったやつを元にSprite.Createする
方法3. 初回だけResources.Loadして、次からは最初に作ったやつを元にInstantiateする
方法4. 毎回外部ファイルをFileStreamで読み込んでSprite.Createする

ソースコード
https://github.com/Meiling-N/LoadTest/blob/main/Assets/Script/Main.cs

計測結果

読み込む画像は正方形とする
表の値は平均ロード秒数
-は未計測、~は途中まで計ってやめた目視最低値

手法\1辺の長さ 32 128 512
1 0.0074 0.0073 0.0072
2 0.5152 4.0 ~ 57.5 ~
3 0.0937 0.0946 0.0948
4 2.0 ~ - -

参考: ロードにかかった最大秒数

手法\1辺の長さ 32 128 512
1 0.0120 0.0112 0.0127
2 - - -
3 0.1075 0.1188 0.1105
4 - - -

考察

Sprite.Createを毎回するのは論外
ロードにかかった最大秒数が計測された周は最初の方に集中しているっぽい
何かしら最適化が働いてるが、OS側だろうか
なんにせよ変にこちら側から小細工すると遅くなるっぽいので
使うならResources.Load一択になる




自動化案の方針

こうなると外部ファイルを参照せず、Resourcesフォルダに修正した画像を入れる, インポート時に自動で設定をするなどの自動化案が考えられるが
Resourcesフォルダを多用すると起動が遅くなるらしい

これによると
Assets, Resources and AssetBundles - Unity Learn
Resourcesは非推奨だが、このチュートリアル自体も非推奨になっている
なんでやねん

一応これを使えと言っている
Addressables | Addressables | 1.21.2
サンプル
GitHub - Unity-Technologies/Addressables-Sample: Demo project using Addressables package
なおUnityのマニュアルではAssetBundleかAddressablesを使ってみたら?みたいな位置づけ
Loading Resources at Runtime - Unity マニュアル
どっちを使えというのかね

なんにせよ、試作じゃないならResourcesから脱却した方がいいらしい
そうなると、どう自動化するかも変わってくるので、こいつらがなんなのかの確認がいる

まぁ自分の規模じゃ気にする必要ないと思うけどね!




nsan.hatenablog.com

ああああ

きっかけ

「立っている物は倒れる」
みたいな、どこぞの重工の掲示物の危険予知6、7項目みたいに
ソフトウェアを開発するうえでなんか危険指標みたいなの作れないかなーって思った

それで

あちらは直接人命に関わる一方、ソフトウェアの多くはまぁ直接人命には関わらない
なので危険予知の、「XXいる物はXXする」「ので巻き込まれて死ぬかもしれかんから気を付けろ」の後者を
「ので巻き込まれてソフトウェアが停止するかもしれんから気を付けろ」みたいに捉えて考える

停止とは

そもそも、ソフトウェアが停止する時は
1.ない
2.違反
3.事故
4.未知
かなって思った

例示すると
1.は、Null、できない、見つからない、足りない、終わらない、とれない、できてない
2.は、範囲外、定義外、長さが違う、中身がおかしい
3.は、デッドロック、謎の実行環境、実行中の遮断
4.は、ちょうちょ・・・、メテオ、高度な柔軟性を以て対応する
みたいなの

ちょっと考えた

1. 確保したものはなくなる
2. 手順は忘れられる   
3. 隠れている物は深い
  

「ので巻き込まれてソフトウェアが停止するかもしれんから気を付けろ」
1.はメモリとか、2.は未来の自分とか、3.はライブラリとか

結局

通化するには拾う範囲が多すぎる・停止原因も複雑になりそうなので、簡易な原則としてまとめるの難しいなーってなった
まぁ統計とったら最初に気を付けること、みたいなのはできるかもしれない
ちなみに、こういう原則があるかを碌に調べてないので既にあるかもしれない

CPU換装時のチェックリスト

少ないけどとりあえず置いておく

物理
  1. CPUとマザーボードの相性
  2. マザーボードのメモリの互換性
  3. マザーボードの大きさ(ガワに入るかどうか,ネジの位置)
  4. 現在つながっている配線の状態(あとでつなぎ直せるように)
  5. 電源
  6. SATAの数
その他
  1. VSTプラグインなどの,CPUに紐づいて認証されているライセンスの管理
  2. OSのライセンス管理

unityで自作fogをやりたい

はじめに

調べてもunityが用意してくれてる奴を使おうねーってのばっかり
俺は自作したいの!!!!!!!!

環境

unity 2021.2.12 URP

やりたいこと

カメラから見たときに,奥行に応じて画面に青みをつけたい
奥に行くほど青く,手前ほどそのままの色
オブジェクト毎じゃなくて,画面全体に適用したい

結果

  1. URPのプロジェクトを作った時についてくるForwardRenderer設定にあるRenderingのDepthPrimitingModeをForcedに変える,ついてこなかったらProjectフォルダの中で自分でURPAssetを作って設定する
  2. ProjectSettingsのGraphicsのSRPSettingsにさっき作ったのを入れる
  3. 使うカメラのPostProcessingをONにする
  4. 望んだグラフィック表現に辿り着くために 拡張性の高い「URP」で作るハイクオリティな独自表現 - ログミーTech の通りにクラスを作る
  5. 以下のようにshaderを書いて,カメラの色と深度をとって処理する
sampler2D _CameraDepthTexture;
sampler2D _CameraColorTexture;
v2f vert(appdata_base v){
    v2f o;
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    return o;
}
fixed4 frag(v2f i): SV_TARGET{
    fixed4 depthCol = tex2D(_CameraDepthTexture, i.uv);
    fixed4 col = tex2D(_CameraColorTexture, i.uv);
    float coef = clamp(saturate(10 * depthCol.r), 0.3, 1);
    col.rgb *= coef;
    return col;
}

※DepthPrimitingModeをAutoにする場合は,URPAssetのDepthTextureをONにしないと取れない

するとこうなる

青みがかってないけど,奥行きに応じた色の変化ができたので終了

懸念点

このままだと常時適用されるけど,
レンダーパイプラインの切り替え - Unity マニュアル
試してないけど,これでやれそう


終わり

                                          • -

以下は試行錯誤推移

経過観察

最初は霧(bloom)かなって思ったけど,どうやらfogの方がそれっぽい
霧はmist・fogなのに一体何を言っているんだ

描画する直前の色に深度値に応じた値を入れられればいいなぁって思った
その辺調べるとどうもPostEffectとかなんかその辺らしいよ?

とりあえず,URPだと自分でレンダリングパイプラインいじらないとだめらしい

いじっていれました

参考:
【Unity】Universal Render Pipelineで独自のレンダリングパスを追加する - LIGHT11
(他にも色々見た気がするけど覚えてない)

                                          • -
  1. カメラのPostProcessingをON
  2. URPを立ち上げたときに入ってるデフォルトのURPAssetの設定のDepthTextureとOpaqueTextureをON

なんかよくわからんけど,Blitで書き込みができるらしいので
こんなスクリプトを追加した

~
Material mat;
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) {
    ~
    var camera = renderingData.cameraData.camera; 
    var cmd = CommandBufferPool.Get(cRenderTag);
    cmd.Blit(renderingData.cameraData.targetTexture, camera.activeTexture, mat); // カメラにマテリアルを適用
    ~
}

マテリアルのシェーダーはこんな感じ

struct v2f{
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
    half depth : TEXCOORD1;
};

v2f vert(appdata_img v){
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord.xy);
    COMPUTE_EYEDEPTH(o.depth.x);
    o.depth *= _ProjectionParams.w;
    return o;
}
sampler2D _CameraDepthTexture;
sampler2D _CameraOpaqueTexture;
            
fixed4 frag(v2f i): SV_TARGET{
    fixed4 col = tex2D(_CameraOpaqueTexture, i.uv);
    col.r = 1;
    col.g = 0;
    col.b = 0;
    return col;
}

確認のためにfragの方は適当に出力してる
これでカメラのテクスチャにmatを適用したのが描画されて画面が真っ赤になる・・・ならないね?なんで?
FrameDebugだと途中で真っ赤になってるのはわかるけど,最終的にはもみ消されてるなんで?

                                          • -

望んだグラフィック表現に辿り着くために 拡張性の高い「URP」で作るハイクオリティな独自表現 - ログミーTech
中身を全部ここの通りにしたら,とりあえず画面変わった
renderPassEventの位置を後ろの方にしないといけないらしい
一方でまだ深度値取れないね・・・あとこれ常時適用されてしまうから
特定の場面のみで適用したい場合の方法を考えないといけない


Unity - Manual: Cameras and depth textures
ここを見る限り,_CameraDepthTextureが有効で
Unity - Manual: Particle System vertex streams and Standard Shader support
ここのサンプルでz値を取ってるのでその通りのやったけど変化なし
Unity のソフトパーティクルのシェーダについて調べてみた - 凹みTips
後はこの辺とか参考になりそう

あー待った,
カメラのテクスチャを取得してる → 全体に描画されている → 画面全体の一枚絵として表示
→ 頂点は一枚絵を構成する画面端の4点のみ → 深度値どこも同じ
なのでは?
FrameDebugの自作レンダーのVerticesが4なので可能性高い
一方でDrawOpaqueObjectsに結構頂点があるので,その辺なら深度値とれるかも

→renderPassEventをAfterRenderingOpaquesにしたけどだめでした

_CameraDepthTextureが画面サイズと全く違う点が気になる・・・
うまくとれてないのか・・・?

見逃していたのかいつの間にか追加されたのかわからんけど,
URPのプロジェクト作った時に付いてくるForwardRenderer設定の
Rendering の Depth Primiting Mode を Forced にすると,
Frame Debug に CopyDepth というPipelineが追加されて
_CameraDepthTextureに画面サイズと同じサイズのTextureが生成されて,
深度値が取れるようになった
前見たときこんな設定なかったような・・・・・・・?
シェーダーに
sampler2D _CameraDepthAttachment;
っていうのを追加したあたりで出たのかな・・・・・・全く関係ないかもしれないけど
コメントアウトしても設定は出たままなので関係ないかも

どちらにしろできたので,ここの記事の最初の方に手順記して終わり

3Dポリゴンで面はどうやって張ってるのか

nsan.hatenablog.com
の続き
頂点の移動方法はわかったが

どうやって面を塗りつぶしているのだろうか

2021/11/29
探した結果

  1. Scan Line Algorithm
  2. Flood Fill Algorithm
  3. Boundary Fill Algorithm

あたりが面を塗りつぶす主なアルゴリズム・・・・・らしい?
本当かどうかわからない
どうもペイントツールとかで使われる用っぽいし,ゲームなどのグラフィック用なのかわからない

参考文献
Polygon Filling Algorithm
【第2回】点の多角形に対する内外判定|【技業LOG】技術者が紹介するNTTPCのテクノロジー|【公式】NTTPC
ブレゼンハムのアルゴリズム - Wikipedia
専用VLSIプロセッサ:専用VLSIプロセッサの具体例:2.3 VLSIグラフィックス・プロセッサ - 「情報処理」 31巻4号
Flood fill - Wikipedia


とりあえずレンダリングパイプラインという天啓が降りてきたのでそっち方面へ
どうやら,ラスタライゼーションの中のピクセルシェーディング処理で各ピクセルの値が決定されるらしい
(ピクセルの値群をフラグメントって呼ぶらしい)

【シェーダ】レンダリングパイプライン
グラフィックスパイプライン - Wikipedia
ラスタライズ - Wikipedia
説明しよう!シェーダーとはッ! - o_healerのブログ
その5 ピクセルシェーダプログラムの基礎

2021/12/14
とまぁ資料を集めたんだけど,どうも最近めっちゃ変わってるっぽいのか
あるいは自分の集め方が下手なのかわからないが,同じような手順を見かけない
どうもかなり複雑になってる様子,もしくは自分が何もわかってない
知識がないと方向性も分からず大変だわ

UnityのURPやHDRPのように,どうもレンダリングパイプラインにも色々種類があるらしい
そら一元化できねぇわ

とりあえず,この辺でも読んでみるかと思ったが無理
Real-Time Rendering Resources

HLSLとかGLSLとかあるらしい?

  • >どうやらC,C++pythonのような言語名があるように,これらはシェーダーのプログラム用言語の名前らしい
  • >ぱっと見,HLSLの書き方はCに見えるが,細部で異なっている

【シェーダ】HLSLの基本

上記のサイトではピクセルシェーダーでは複雑な処理は行わないようにしてと書いてあるが
[Unity]URPで始める!シェーダー入門 - Qiita
こっちは絵作りに関する様々な手法をここで表現することができる,つまり複雑なこともできると読み取れる
どっちやねん

  • >GPUの性能がいいほど沢山できるよってことらしい

神様の助言に従ってGPUのベンダー資料を探りにいってみる
これがわかりやすそう,2015年の記事だけど
https://developer.nvidia.com/content/life-triangle-nvidias-logical-pipeline

いやこっちの方がいいな
https://stanford.edu/class/ee267/lectures/lecture2.pdf
https://stanford.edu/class/ee267/lectures/lecture3.pdf
1. モデルの全頂点を移動させる
2. カメラの画面上で切り取る
3. ディスプレイ上のどの点に何色を表示するか決定する
という手順
この3.で何色を表示するかに光源や反射光などが使われる模様

赤メッシュと青メッシュがあったとしたら,それぞれのメッシュにそれぞれの色をどうやって割り当てているのか・・・
いや最初から頂点情報とセットで渡してるって考えた方がいいか,GPUにどんな処理をさせるかってのはシェーダー上で書けるし

で,ここを改めて見ると第4章が何を言ってるのかわかるようになった
https://tokoik.github.io/gg/ggbook02.pdf

ので,「どうやって面を塗りつぶしているのだろうか」に対する基本的な方針における回答としては

0. 頂点の移動
0. 表示したい位置で切り取り
ここまで頂点の移動に関すること
1. メッシュをディスプレイ上のどの点で表示するか決定
2. メッシュに設定されてるシェーダーを元に色を生成(メッシュ内の塗りつぶし)

ということらしい
まぁ,他にも間にめっちゃ色んな処理が入ってるけどやってる流れは大体一緒っぽい
あと,最適化したいならGPUの動きについて知る必要がありそう



以上

頂点配列から画面にポリゴンが映るまで

正確性に大きく欠けるはずです☆
中身を全然理解してなさそうな処理の流れ画像
f:id:Nsan:20211115224405j:plain


シェーダーや色付けの部分が間に合わなかったので次回へ


以下お勉強推移


2021/10/13
お決まりの順番で頂点配列をライブラリとかに渡して,変換行列を設定すると
なんかよくわからんけど画面上にポリゴンが表示される

ライブラリとかOSとかなにやってんの?

2021/10/24

「基本のポリゴンの形はなんで三角形なの?」

から始める

これといった回答は見当たらないが,どうやら処理速度を上げようとすると三角形になるらしい

1.任意のN点を通る形を表示しようと思えばその形を表示ができる(?)が,計算は複雑になる
2.任意の3点を通る平面(三角形)は一意に定まる
3.任意の4点を通そうとすると色んな形になる(平面・三角錐など)
4.同じアルゴリズムで大量に処理できる三角形が扱いやすい
5.三角形だとデータ数も多くならないらしい?

以上から

「どうやって三角形をディスプレイに表示してるの?」

長くなりそうな予感がする

まず,3次元空間上の三角形とすると
自分が指定した空間3点の座標をスクリーン(今お前が見てるディスプレイ)上の座標に置き換える作業が必要,
3点に囲まれた平面に色を塗る必要もあるけど

「どうやってスクリーン上の座標指定してるの?」

LED(発光ダイオード)/LED表示器/ドットマトリクスLED 秋月電子通商-電子部品・ネット通販
この辺りの発展形だと思ってる

1.プログラムによってスクリーン上の各点に指定した色で光るような信号値を送ってるのだと思う
1-1.プログラム上の0とか1とかの値がどうやってアナログ信号値になるかは,D/A変換という言葉で終わらせておく
2.ラスタースキャン方式により,スクリーンの「左から右」「上から下」方向へ順に信号値を読み取り光が出力される
2021/11/1
2-1.この読み取りはプログラム側からではなく,ディスプレイ側が勝手に行う
2-2.プログラムとディスプレイとでずれないように表示するために同期が必要となる(垂直同期・水平同期,リフレッシュレート)

3.実際にはCPUからGPUのVRAMへ画面データを送り,同期されたタイミングでGPUがディスプレイに信号を送っていると思われる
(3-1.画面のちらつき防止のためのダブルバッファリングとかでも交換タイミングはGPUが勝手に決めてる?)

以上から
・スクリーン上の座標はプログラムで直接指定できる
・プログラムで指定したスクリーン上の座標に指定色を出す信号を送って表示している
高級言語からGPUのVRAMへどうやって格納しているかはOS・ライブラリ・GPU等に依存している?

が結論

参考
表示方式|液晶の世界:シャープ
液晶ディスプレイ - Wikipedia
グラフィックボード(CRTC)その1
【特集】NVIDIAのさまざまなディスプレイ垂直同期方式をもう一度整理する - PC Watch
VRAM - Wikipedia
VRAMへのアクセスをプログラムで書くと - 『すぐに使える!液晶搭載マイコン・モジュール』blog
Linux - PCのVRAMに直接アセンブリ言語でデータを書き込む。|teratail



2021/11/8

「どうやって空間上の座標からスクリーン上の座標へ変換してるの?」

モデル座標系(頂点情報) -[モデル変換]-> ワールド座標系 -[ビュー変換]->ビュー座標系-[投影変換(射影変換)]->デバイス座標系 -[ビューポート変換]-> スクリーン座標系
という手順らしい
主に行列による変換だしggればすぐ出てくるので省略

参考
http://www.cgg.cs.tsukuba.ac.jp/~endo/lecture/2020/cgbasics/03/03.pdf
グラフィックス科学演習 / Graphics Science Seminar
モデルビュー変換


「オブジェクトの描画順序は?」

適当にやると奥にあるはずのオブジェクトが手前に見えたりしてしまう

スキャンラインやレイトレーシングなどあるけど,Zバッファ法がゲームに使われてるらしい
こちらも資料豊富なので省略
法線算出やバックフェースカリング(隠れ面消去)なども資料ある
なお,不透明と半透明は,わけて描画するらしい

参考
http://www.cloud.teu.ac.jp/public/MDF/toudouhk/lectures/cg2014/08_rendering1/08_rendering1.pdf
【Unity】Zバッファとレンダリング順の仕組み - Qiita
シェーダを書けるプログラマになろう #1 シェーダを理解しよう - Unity道場2019 2月 - YouTube



以上から「どうやって三角形をディスプレイに表示してるの?」は

  1. 頂点情報を取得する
  2. 各種変換を通してスクリーン座標系にして画面に映すデータを作る
  3. GPUのVRAMへ画面データを送り,同期されたタイミングでGPUがディスプレイに信号を送る
  4. ディスプレイが更新を行い,表示される

なお,1.2.3.はCPUとGPU上で反復横跳びする
が結論

参考
DirectX11によるポリゴン描画のための準備処理実装方法【DirectX11】
https://tokoik.github.io/gg/ggbook02.pdf
ゲームグラフィックス特論 A / B − 授業概要


「なんで頂点インデックスがいるの?」

複雑なモデルの三角形メッシュ全部の頂点を保存しようとすると
頂点が重複するし,動かそうとすると処理大変
なので,頂点の位置とメッシュを作るのに必要な頂点をわけようってことらしい

よって
頂点情報の重複消去
が結論

参考
その30 気になる頂点インデックスの意義
ポリゴン