Yaminabe

らくがきぶろぐ

てすと:Yaminabeちゃんは生まれたばかりのブログです みんなで仲よく使ってね

カテゴリ: Unity3d

 

 

ヒープメモリやらメモリ管理で検索が多いので記事にしてみたのですが 久しぶりのブログ更新なので後半部分をまとめたところで力尽きました。力尽きました

メインは海外サイトの翻訳なのですが どうにもわかりにくい。メモリ管理で検索するんだからわりと初級?だもの この説明では難しいことでしょう

後日改めて、 もう少しわかりやすくまとめた前半部分を追記しますのでこのページにリンクを貼らないで置いてください。

<文字列制限があるので記事を分割するとアドレスが変わる可能性があるのです>

うちのブログだと 8割がたゲームメーカーからのアクセスなのであんまり初心者むけ記事は見てもらえないような気もするんですけどね... (´・ω・`)

 

 ヒープメモリとは?                                              

更新予定

■とりあえずはここらへんを読んでおいてください

  • Unity - マニュアル- 自動メモリ管理を理解する
  • 【Unity】Unite 2015「Unity パフォーマンス・チューニング」レポート ? Qiita

     

     

     不必要なヒープ割り当ての一般的な原因               

    この元ブログ記事では以下の様な内容でメモリ管理について論じています

    1. メモリ管理とメモリリークの一般的な原因
    2. ユニティプロファイラによるメモリリークの検出
    3. C#でのオブジェクトプーリング

    ここから元記事の意訳

    • この最初の投稿は.NETとMonoのガベージコレクトされた世界のメモリ管理の基本について議論します。またメモリリークのいくつかの一般的な原因について説明します。
    •  
      2番目の投稿はメモリリークを発見するためのツールについてです。Unityプロファイラは、この目的にとって素晴らしいツールですが、それはまた高価です。そこであなたが唯一の無料ツールでメモリリークを発見する方法をお見せするために、.NETの逆アセンブラと共通中間言語(CIL)について議論します。
    •  
      3番目の投稿は、ふたたびC#のオブジェクトプーリングを議論します。ここでも焦点はUnity/ C#の開発で生じる特定のニーズです。

     

    ■ガベージコレクションの限界

    最近のほとんどのオペレーティング・システムでは、スタックとヒープに動的メモリを分割します。多くのCPUアーキテクチャ(PC/Mac そしてスマートホン/タブレットが含まれます)でこのような命令セットがサポートされています。c#では値の型を区別することでこれらをサポートしています(単純な組み込み型などとして宣言されているユーザー定義型  enum  または  struct)と参照型(クラス、インタフェース、およびデリゲート) 値型はスタックに参照型はヒープに割当てられます。スタックは新しいスレッドの開始時に設定された固定サイズを有しています。これは通常は小さくて たとえばwindowsのデフォルトの.Netスレッドは1Mb。このメモリはスレッドの主な機能とローカル変数をロードし メイン関数から呼び出される関数(それらのローカル変数)をアンロードするために使用されます。その一部は動作を高速化するためにCPUのキャッシュにマッピングすることができます。コールが深すぎないかローカル変数は巨大すぎないかといったスタックオーバーフローを恐れる心配はありません。スタックのこの使われ方は構造化プログラミングの概念と整合することを確認してください。

     

    オブジェクトがスタックに対して大きすぎる場合またそれらの機能がスタックに長く残ってしまった時にヒープが発生します。ヒープはそれ以外のメモリの箇所で 通常はOSとプログラムの規則の要求通りに増加します。一方スタックにある場合はメモリ管理をするのは簡単で(フリーなメモリの場所を記憶するためにはポインタが使用されます) ヒープを管理することは,ヒープが断片化した場合すぐに割当てられたオブジェクトからオブジェクトを開放させる要求をださなければならないことは明らかで,すべてのスイスチーズの穴のような場所を覚えることに気を止めなければならず 全く楽しくありません。そこで自動メモリ管理を導入します。 自動割当てのタスクは 主にあなたにとってのすべてのチーズの穴のようなメモリ断片を追跡する簡単なもので実質すべてのプログラミング言語でサポートされています。 自動割当てをする決定の多くの困難なタスクを特にあなたが心配する必要はなくオブジェクトは割当て解除の準備ができています。

    この後者のタスクは、ガベージコレクション(GC)と呼ばれています。あなたのランタイム環境を伝える代わりに 実行時に一定の間隔でオブジェクトへのすべての参照を追跡し確定後オブジェクトが破壊されてメモリが解放されたときに おそらくコードからオブジェクトに到達できなくなります。ガベージコレクトは未だに学者によって活発に研究されていることが ガベージコレクトのアーキテクチャが。Netフレームわーっクから大幅に変更と改善をされている理由です。またUnityはMONOの最新バージョン(2.11 / 3.0)にデフォルト設定はありませんが、その代わりに バージョン2.6を使用しています(正確には、私のWindowsの2.6.5は、Unityの4.2.2 がインストールされています。Unty4.3についても同様です) あなたが自身でこれらを確認する方法に不明な点があるようであれば 次の投稿でそれを説明していきます。

     

    バージョン2.6以降のMONOに導入されたGC関連は主要な改変のひとつです。新しいバージョンがGenerational GC(世代別GC)を使用しているのに対し バージョン2.6ではまだあまり洗練されていない ベームガベージコレクタを使用しています。最近の世代別GCはゲームなどのリアルタイムアプリケーションのために(制限内ですが)良好に動作します。ベームスタイルのGCは一方で比較的まれな間隔(通常は一回あたりのフレームのゲームに比べてはるかに少ない頻度)でヒープ上のガベージごみを徹底的な検索を実行して動作します。これによって一定の間隔でフレームレートの低下が引き起こされる圧倒的な傾向があり プレーヤーは迷惑を蒙(こうむ)ります。UnityのドキュメントではFPSが低下する状態の場合にはSystem.GC.Collect()を呼ぶことを推奨しています(例えば、新しいレベルのロードやメニューを表示する時) しかし多くのゲームの種類においてこのような機会はまれにしか発生せず あなたがGCのをしたい時にガベージコレクトを拒否するかもしれません。あなたの唯一の選択肢は勇敢にメモリを自分で管理することで、それについて残りの2つの記事で述べています。

     

    ■メモリマネージャを適切なものに

    ユニティ/ .NETの世界では「メモリを自分で管理する」ことを意味するかについて明確にしてみましょう。メモリ割当てがされる方法に影響を与えるためのあなたの力は(幸いなことに)非常に限られています。あなたは クラス(常にヒープ上に割り当てられた)または  構造体(クラス内に含まれていない限りスタック上に割り当てられます)のカスタムデータ構造を選択し採用することができます。あなたがより多くの魔法の力が必要な場合は、C#のUnsafe コードのキーワードを使用する必要があります。しかしUnsafe コードは、UnityのWeb Player及びおそらく他のいくつかのターゲットプラットフォームで実行されないことを意味している検証できないコードです。このことと  その他の理由により安全ではありませんので使用しないでください。

    ※Unite EUROPE2015のKEYNOTEを観る限りではUnsafeコードは普通に使用されているようですが?

    そのためスタックの上記の制限およびC#の配列がためだけの糖衣構文であるためのSystem.Array(これらはクラスです)自動ヒープ割当てを避けることはできません。不要なヒープの割り当てを何によって避けるべきかをこの記事の次の(そして最後の)セクションで知ってください。

    メモリ割当て解除されるまではあなたの及ぶ力は限定的です。実際ヒープされたオブジェクトの割り当てを解除することができる唯一の方法はGC(ガベージコレクト)であり その働きはあなたから遮蔽されています。ヒープ上のオブジェクトのいずれかへの最後の参照がスコープ外になったときにGCがその前にそれらに触れることができないため、何らかの影響を与えることはできます。定期的なガベージコレクション(あなたが抑制することができない)が解放するものがないときには非常に高速になる傾向があるため この制限された力は、たいへん実用的な関連性を持っていることが判明しました。この事実は様々なアプローチの基礎となるオブジェクトプーリングについて3番目の投稿で議論します。

     

     

    ■foreachループを避けるべきでしょうか?

    foreachループを使用する代わりにforまたはwhileループを使用するべき理由は、foreachは実際には単なるシンタックスシュガー(糖衣構文:読み書きのしやすさのために導入される構文)で、以下のようなコンパイラの前処理コードだからです。

    foreach (SomeType s in someList)
        s.DoSomething();

    ...次のように:

    using (SomeType.Enumerator enumerator = this.someList.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            SomeType s = (SomeType)enumerator.Current;
            s.DoSomething();
        }
    }

    foreach はenumeratorオブジェクトを生成します。 System.Collections.IEnumeratorインターフェースのインスタンス シーンの裏側で このオブジェクトはスタックとヒープのどちらに生成されるのでしょうか?両方が実際に可能であるため これは大変よい質問です。もっとも重要なことはSystem.Collections.Generic の namespace (List, Dictionary, LinkedList, etc.)は構造体を返すのに GetEnumerator()メソッドは大変適しています(参考記事が書かれた時点ではUnityはMono 2.6.5を使用)

     

    foreachのループを回避する必要がありますか?Unityでコンパイル可能なC#のコードでそれらを使用しないでください。foreachを標準のgeneric collections (List etc.) を用いて反復処理するために、あなたがC#のコードをコンパイルに最近のコンパイラを使用していますか?Visual Studioのほかに無料の.NET Framework SDKが適正であることを前提とし(ただし、確認はしていない)Monoと最新バージョンに付属しているMonoDevelopも同様に適正とします。外部コンパイラを使用して foreachループを反復するための他の種類のコレクションをどうすればいいでしょうか? 残念ながら答えはありません。 foreachを安全に使用するためどのコレクションを使用するかあなた自身が見つけ出すためのテクニックの使用法を次回のブログポスト元記事2で論じてみます。

     

    ※2

  • http://www.gamasutra.com/blogs/WendelinReich/20131119/203842/C_Memory_Management_for_Unity_Developers_part_2_of_3.php
  • http://monodevelop.com/

     

    ■あなたはclosuresやLINQを避けるべきでしょうか?

    C#では匿名メソッドやラムダ式(かなり互いに同一)が提供されていてdelegateキーワードと=>演算子 を用いてそれらを作成することが出来ます。これらはしばしば便利なツールであり List<T>.Sort()) や LINQ といったライブラリ関数を使用するときたいへん重宝します。 匿名メソッドとラムダ式は、メモリリークを起こしているでしょうか? それは依存しています C#コンパイラには、実際にそれらを処理する2つの非常に異なった方法があります。違いを理解するために次のコードを考慮してください。

     

    int result = 0;
        
    void Update()
    {
        for (int i = 0; i < 100; i++)
        {
            System.Func<int, int> myFunc = (p) => p * p;
            result += myFunc(i);
        }
    }

     このようにスニペットmyFuncというdelegateを1フレームあたり100回生成する計算を実行します。しかしながら Monoはヒープ割当てを最初にUpdate()関数(※筆者の環境で52Byte)が呼ばれた時だけ実行します。サブシーケンスでヒープ割当ては実行されません。どうなっているのでしょう?コードリフレクタ(part2を参照)を使用すると  C#コンパイラはmyFunc型の静的フィールドでSystem.Func  型、int型 >を含むクラスでUpdate()関数を単に置き換えることがわかります。 このフィールドには明示的な名前 f__am $ cache1を取得しています  (システム上で多少異なる場合があります)。つまりdelegatorは一度だけ割り当てられて、その後キャッシュされてるのです。

    今度は、デリゲートの定義にマイナーチェンジを作ってみましょう:

    System.Func<int, int> myFunc = (p) => p * i++;

     

    closuresは関数型プログラミングの柱です。closuresはデータに関数を結びつけます、より正確には関数外で定義される非ローカル変数に結びつけます。 このmyFuncの例の場合'p'はローカル変数ですが'i'はUpdate()関数に属する非ローカル変数です。 C#コンパイラはmyFuncをアクセスと非ローカル変数を更新することができるように変換する必要があります。 リファレンス環境にあわせて新規にクラスを宣言することでmyFuncの生成を実現しています。 このクラスのオブジェクトは、for-loopを通過するたびに割当てられるため(※筆者の環境ではフレームごとに2.6kb) 突然巨大なメモリリークに襲われます。

    もちろん closuresや他の言語機能がC#3.0で導入された主な理由はLINQです。closuresがメモリーリークに繋がることができればゲームでLINQを使用しても大丈夫でしょうか?LINQの一部はiOSなどの実行時コンパイラをサポートしないOS上で明らかに動作しません。メモリ管理の側面からLINQはとにかく良くありません。

  • http://answers.unity3d.com/questions/376884/using-linq-when-building-to-ios.html
  • クロージャ ? Wikipedia

     

    次の信じられないような基本的な表現:

    int[] array = { 1, 2, 3, 6, 7, 8 };
    
    void Update()
    {
        IEnumerable<int> elements = from element in array
                        orderby element descending
                        where element > 2
                        select element;
        ...
    }

    毎フレーム68バイトを割当て( Enumerable.OrderByDescending() が28バイト、 Enumerable.Where()で40バイト)!

    ここでの犯人はclosuresではありません 拡張メソッド IEnumerable:LINQは最終的な結果を出力するために 中間の配列を作成する必要があり そのあとそれらをリサイクルするシステム領域を持ちません。(※私はLINQの専門家ではないので リアルタイムの実行環境で安全にそれらのコンポーネントを使用する方法はわかりません)

    ■コルーチン

    あなたがStartCoroutine()経由でコルーチンを起動した場合は、暗黙的にunityのインスタンスコルーチンのクラス(私のシステムで21バイト)とEnumerator(16バイト)の両方を割り当てます。yield's や resumesコルーチンは何も割当てをしません そこでメモリリークを回避するためにはゲームが実行されている間StartCoroutine()の呼び出しを制限してください。

    void Update()
    {
        string string1 = "Two";
        string string2 = "One" + string1 + "Three";
    }

    ■ストリングス

    C#とunityのメモリに関しては文字列に言及しない訳にはいきません。文字列は変わっていて、ヒープ割当てでありながらイミュータブルです。2つの文字列を連結するとき(これらの変数や文字列定数で)ランタイムは 結果を含む少なくとも1つの新しい文字列オブジェクトを割り当てることがあります。String.Concat()これはFastAllocateString()と呼ばれる外部メソッドにより効率的に行われます。(ただし上記の例ではシステム上の40バイト)。 変更または実行時に文字列を連結する必要がある場合にはSystem.Text.StringBuilderを使用します。

    オブジェクト指向プログラミングにおいて、イミュータブル(immutable)なオブジェクトとは、作成後にその状態を変えることのできないオブジェクト のこと

    ■ボクシング

    時々データがスタックとヒープを移動する必要があります。例えば 次のように文字列のフォーマットを設定する場合です:


    string result = string.Format("{0} = {1}", 5, 5.0f);

    次のシグネチャを持つメソッドを呼び出しています。
    public static string Format(
    	string format,
    	params Object[] args
    )

    言い換えると、整数「5」および浮動小数点数「5.0f」はSystem.ObjectがFormat()を呼び出した際にキャストする必要があります。 しかし、オブジェクトが参照型なのに対して他の2つは値型です。 C#はこのようにヒープ上の割り当てられたメモリに値をコピーすることがあり、Format()はint型とfloat型のオブジェクト参照を新しく作成します。 このプロセスがボクシング、 反対の動作をアンボクシングと言います。 このstring.Format()のビヘイビアは問題となるかもしれません すなわちヒープメモリからとにかくそれを取り除く必要があります(新しいstring のために) しかしボクシングは期待される場所に出現するわけではありません。悪名高い例として 等価演算子"=="を実装するとき (例えば、複雑な数を表す構造体)

    ※このような場合のボクシングを避ける方法についての記事をリンク先で読むことが出来ます。

  • http://stackoverflow.com/questions/10390782/why-cant-we-override-equals-in-a-value-type-without-boxing

     

    ■ライブラリメソッド

  • Dictionary<K, V>.KeyCollection と Dictionary.ValueCollectionはクラスで構造体ではありません。 これらはつまり"foreach (K key in myDict.Keys)..."に16バイト割りつけるということです。
  •  List.Reverse() は標準のインプレース配列反転アルゴリズムを使用しています、それがヒープメモリを割り当てないと考えるかと思いますが、少なくともMono 2.6バージョンでは間違いです。 ここで拡張メソッドは.NET/Mono バージョンで最適化されないときに代用出来るかもしれません。少なくともList.Reverse()を使用するのと同じやり方で、ヒープ割当てを避けるための管理がなされます。

    public static class ListExtensions
    {
        public static void Reverse_NoHeapAlloc<T>(this List<T> list)
        {
            int count = list.Count;
    
            for (int i = 0; i < count / 2; i++)
            {
                T tmp = list[i];
                list[i] = list[count - i - 1];
                list[count - i - 1] = tmp;
            }
        }
    }

     

     

    ここから、これまでの考察をまとめた別記事の意訳です

     ■Unity、C#と.NET /モノラルでメモリ使用量を削減             

  • Reducing Memory Usage in Unity, C# and .NET-Mono - Andrew Fray

     

    iOSの上のUnityはMonoのヒープマネージャの初期のバージョンを使用しています。このマネージャは、パッキングを行いませんので、ヒープメモリが断片化された場合は新しいメモリを確保してしまいます。私はUnity開発者がこの問題を回避するために新しいヒープマネージャで作業している印象なのですが、今のところメモリリークを持つゲームは増え続けるメモリを消費してしまいます。C#は,すばやく読みやすさを犠牲にすることなく強力なコードを記述することができる楽しい言語です。しかしこの方法の欠点は自然なC#のコードを書くことがガベージコレクションの多くを生成してしまうことです。これを回避する唯一の方法はヒープ割当てを排除または低減することです。私は機能性を低下させることなく これを行うための便利な方法をリストにまとめました。最終的な効果として あなたのC#コードは,はるかにC ++のように見えC#の力の一部を失ってしまうものの それにより生きてきます副次効果としてヒープアロケーションは本質的にスタックアロケーションよりもよりCPU集約型なので、おそらく同様にいくつかのフレーム時間を節約できます。あなたの努力目標のためにユニティプロファイラは多くのメモリ割当てを作る作業を助けてくれます。情報は多くはないですが。プロファイラを開きゲームを実行し、CPUプロファイラを選択して、問題箇所をソートするためのGCアロケータカラムをタップします。はじめにそれらの機能にこれらのガイドラインを適用してください。

     

    foreach()を使用しないでください。

  • GetEnumerator()を呼び出しlist型にヒープ上のenumeratorを割り当てるのをすぐに止めてください。for(;;)構文に,より詳細なC ++スタイルを使用することが必要になります: あなたが配列上にforeach-ingしなければ,それは何もメモリを割当てません。私はそれがfor(...){}のための構文糖衣(読み書きのしやすさのために導入される構文)になった特殊なケースだと推察します。

    Ian Horswill氏このポイントを指摘していただき有り難うございます。

  • Ian Horswill http://www.cs.northwestern.edu/~ian/
     

     

    strings文字列を避けてください。

     

    文字列は.NETでは不変でヒープメモリ領域に割り当てられています。UIのためC言語の様にその場で それらを操作することはできません StringBuilders で、できるだけ文字列への変換を遅らせてメモリ効率の良い方法で文字列を構築します。あなたはリテラルがメモリ内の同じインスタンスを指す必要があるためキーとしてそれらを使用することができますが、あまりにも多くの操作はしないでください。

  •  http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.71).aspx
  •  C#入門-リテラル - WisdomSoft

    構造体を使用してください。

  • MonoのStruct型はスタック上に割り当てられています、ユーティリティクラスを持っていれば構造体作成にスコープを残すことはありません。任意のコピーのコストを回避するためにrefとパラメータの前に付ける必要があります。構造体は値で渡されることを覚えておいてください。

     

    スコープ結合を固定サイズ配列の構造体に交換してください。

     

    固定サイズの配列を持っている場合スコープを残さずメンバ配列を再利用できます。またはそれをミラーフィールドを持つ構造体を作成することができます メンバー配列に置き換えることのいずれかを検討してください。 私は4つのフィールドを持っているスプラインクラスのControlList構造体を呼び出すときはいつもVector3 [4]に置き換えます。そしてインデックス基準のアクセスのためにthis[]プロパティを追加しました。それは頻繁に呼び出される関数だったので大量のメモリ割当てを回避できました。

     

    リストを受け渡す際refキーワードを使用した参照渡しが好ましいです。

    あなたはヒープメモリに渡すリストを必要とするのが正解で、これで何も変わらないように聞こえますか? ですがこれにより必要に応じて最適化を可能にします。

  •  パラメーターの引き渡し (C# プログラミング ガイド)
  • 方法 - メソッドに構造体を渡すこととクラス参照を渡すことの違いを理解する (C# プログラミング ガイド)

     

    ■頻繁な呼び出しをする関数をメンバ変数としてをローカルストレージにスコープすることを検討してください。 

    毎回大きなリストを呼びださなければならない時,リストにメンバ変数が作成されストレージはフレーム間持続します。 次のフレームにはメモリ領域が不足するのでC#のリストでバッファ領域から削除するためClear()を呼びださなければいけません。それによりコードが酷く読みにくくなるので、わかりやすいコメントが必要になりますが 作業は大幅に軽減できます。

  • 2-2 変数と定数 https://msdn.microsoft.com/ja-jp/library/cc406734.aspx
  • 変数のスコープ http://wisdom.sakura.ne.jp/programming/cs/cs23.html

     

     

    ■IEnumerable拡張メソッドを避けてください。

    ほとんどの便利なLINQのIEnumerable拡張メソッドは、新しい割り当てを作成することは言うまでもありません。私は.Any()が IList<>で呼びだされた時  Count> 0で Virtual関数がメモリ割当てを引き起こす First() とLast()のようなIlist()関数のメソッドも同様だと考えたのですが予想に反していました。この結果とforeach()の制限から あなたのインタフェースにはIEnumerable <>抽象化を避けて代わりにIList <>を使用するべきでしょう。

    ■関数ポインタ使用を最小限に 

    クラスメソッドをデリゲートやFunc<>に代入するとボックス化の原因となりメモリ割当てを引き起こします。私にはボクシングのないメソッドへのリンクを格納するための任意の方法はわかりません。デカップリングに大きな恩恵があるため割当てられたメモリは残り続けますが関数ポインタのほとんどは残してきましたが,いくつかは削除しました。

    ※デカップリング(Decoupling) プログラミングとデザインで、一般的に 再利用され可能な限り少数の依存関係を使用してコードを作成する行為のこと

     

    クローン化されたマテリアルに注意してください。 

    任意のレンダラのマテリアルプロパティを取得する場合、それに何も設定しない場合でもマテリアルを複製します。このマテリアルはGC'dされていません レベルの変更(シーンロード)またはResources.UnloadUnusedAssetsを呼び出したときのいずれかの場合のみクリーンアップされます。マテリアルを調整する必要はありませんが知っていればmyRenderer.sharedMaterialを使用してください。

    ※MaterialPropertyBlock を使用することで改善できる場合があります

  •  

    ディクショナリキーとして生の(未加工)構造体を使用しないでください 

    Dictionary型を使用する時 あなたのキー値が構造体である場合はTryGetValue()またはインデックスアクセサを使用してDictionaryから値を取得することが出来ますが ここでメモリ割当てが発生します。これを回避するには構造体のためにIEquatable を実装します。Dictionaryはいくつかの既定の等値比較をするたびに作成されていると思います。

     ライアン・ウィリアムズhttps://twitter.com/quxmore のおかげで,これを見つけられました。

    Enum.GetValues()またはmyEnumValue.ToString()を使用し過ぎないようにしてください。

    Enum.GetValues(typeof(MyEnum))は呼びだされるたびに配列を割り当てます。Enum.GetNames()も同様です。他の場所同様にUIを扱う際enum変数に.ToStringを加えて使用すべきです。enum値をループなどcachedEnumNames [(int)myEnumValue]に使用するように簡単にこれら両方の配列をキャッシュすることができます。enum型の値を手動で設定している場合 例えばフラグですが、これは動作しません。

     

     

     

     

     

     C# Coroutine WaitForSeconds Garbage Collection tip - Unity Community 

     

  • C# Coroutine WaitForSeconds Garbage Collection tip - Unity Community

     

    追記:メモリ最適化の実装例としてWaitForSecondsコルーチンを最適化してガベージコレクトを抑える記事を簡単にまとめておきます。

     

    詳細はリンク先の記事で確認してください

     

     

     

    A: <Before>"yield return new WaitForSeconds()" は呼び出されるごとにガベージメモリを21バイト消費します

    ("yield return null"なら9バイトのメモリ消費)

      そこで"new WaitForSeconds()" を使用前にキャシュしてヒープ上に確保するメモリを削減してみます。

    これにより一回のIEnumeratorメソッドの呼び出しを9バイトに減らすことが出来ました。

    B:Yieldメソッドを場合分けして辞書登録し Yieldersクラスとして呼び出します。WaitForSeconds()メソッドは直値を指定すると A:の例では”0.1f、0.5f"のフロート型の数値ですが、呼び出されるたびにヒープメモリには新規に数値がキャッシュされ これが9バイトのメモリ消費となってしまいます。

    そこでUnityのシステムが管理する値 WaitForFixedUpdate()に代入される値をstatic型で置き換えることで、値がヒープメモリに複製されないようにしています。

    A:も ガベージコレクト対策としては悪いアイデアではないのでケースに応じて使い分けることが良いかもしれません。

    IEnumeratorメソッドはゲームオブジェクトのスクリプトに含まれることが多いためアタッチされたインスタンスが数百単位で複製される場合もあり 多くのヒープメモリを消費してしまいます。そのためガベージコレクト回避の対策として"Yield〜WaitForSeconds()" まわりを最適化することは有効だと考えられそうです。

     

     

     

    A:

    IEnumerator myAwesomeCoroutine()

    {

        while (true)

        {

            doAwesomeStuff();

            yield return new WaitForSeconds(waitTime);

        }

    }

    WaitForSeconds shortWait = new WaitForSeconds(0.1f);

    WaitForSeconds longWait = new WaitForSeconds(5.0f);

     

    IEnumerator myEvenAwesomerCoroutine()

    {

        while (true)

        {

            if (iNeedToDoStuffFast)

            {

                doAwesomeStuffReallyFast();

                yield return shortWait;

            }

            else{

                dontDoMuch();

                yield return longWait;

            }

        }

    }

     

    B:

    using UnityEngine;

    using System.Collections;

    using System.Collections.Generic;

     

    public static class Yielders {

     

        static Dictionary<float, WaitForSeconds> _timeInterval = new Dictionary<float, WaitForSeconds>(100);

     

        static WaitForEndOfFrame _endOfFrame = new WaitForEndOfFrame();

        public static WaitForEndOfFrame EndOfFrame {

            get{ return _endOfFrame;}

        }

     

        static WaitForFixedUpdate _fixedUpdate = new WaitForFixedUpdate();

        public static WaitForFixedUpdate FixedUpdate{

            get{ return _fixedUpdate; }

        }

     

        public static WaitForSeconds Get(float seconds){

            if(!_timeInterval.ContainsKey(seconds))

                _timeInterval.Add(seconds, new WaitForSeconds(seconds));

            return _timeInterval[seconds];

        }

      

    }

    <呼び出し側>

    必要な時間間隔にするにはYieldメソッドを複数回記述する。

    yield return Yielders.Get(this.updateinterval);

     

     

    【追記】

    『テラシュールブログ』さんでUNITYのパフォーマンス最適化について、まとめられているそうですので興味のある方は参照してください。

    もう一か所、自分の説明不足のところをフォローしていただいたようなので、こちらの記事も参照してみてください。

     

    ■ 更新ついでに近況 … 仕事が一段楽したので近々ブログ復帰予定です.  (10/29)

     

    ■リファレンス

  • Gamasutra- Wendelin Reich's Blog - C# Memory Management for Unity Developers (part 1 of 3)
  • Gamasutra- Wendelin Reich's Blog - C# Memory Management for Unity Developers (part 2 of 3)
  • Gamasutra- Wendelin Reich's Blog - C# Memory Management for Unity Developers (part 3 of 3)
  • Three kinds of generic object pools to avoid memory deallocations in Unity-based games. Released
  • Reducing Memory Usage in Unity, C# and .NET-Mono - Andrew Fray
  • https://unitygem.wordpress.com/memory-management/
  • https://web.archive.org/web/20140624150214/http://msdn.microsoft.com/en-us/library/e59b22c5(v=vs.71).aspx
  • https://web.archive.org/web/20140702030338/http://unitygems.com/wp-content/uploads/2012/12/Stack2.png
  • C#-.NETがやっていること 第二版
  • C#の高速化入門
  • メモリ管理の話_エスキュービズム勉強会20140926
  •  

     

     

    こんばんわです

     

    季節がらまたも台風が 西日本方面からちかづいているとか

    今年は大きめの 『大自然のおしおき』 が押しよせているようですが

    被害が出ないよう気を引き締めないといけませんね

     

    そんな業務連絡

     

     

     

    さて ようやくお仕事のほう一段落しましたが まだ膝に受けた傷が癒えておりません

    長いこと記事も更新していないため かるく翻訳ネタでリハビリをしようと思います。

     

    今回はUnite2013のUnityのモバイルゲームの最適化に関するペーパーを PDFのリンクだけだとさみしいので簡単に訳してみました

    情報としてはちょっと古めかもしれませんが、これからUnityでゲーム制作を初める方は参考にどうぞ。

     

     

     

     

     

     

    □Optimizing Unity Games for Mobile Platforms

     

    Angelo Theodorou
    Software Engineer
    Unite 2013, 28th-30th August

     

    malideveloper.arm.com-downloads-Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms.pdf

     

     

     

     

    □Mali_GPUについて

     

    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_06

     

     

     


    □予備知識


    ドローコールとは何ですか?

    • 基本となるAPI(例えばOpenGL ESの)の関数を呼び出すと、画面上に何かを描画することです

    フラグメントとは何ですか?

    • 一度描画候補に上がったピクセルは表示されてもされなくても(例えば、上書きされる 廃棄されるなど)スクリーン全体に描画されます

    不透明と透明レンダリングキューの違いは何ですか?

    • 最初のキューではオブジェクトはデプステストにしたがって手前から奥に向かって最小限のオーバードローでレンダリングされます

    透明なものはその後に正確さを維持しつつ奥から手前に向かってレンダリングされます

    バッチ処理とは何を意味するのでしょうか?

    • 全体のデータセットの中から似たようなインスタンスグループを1回のドローコールで呼び出しするための処理です


    なぜモバイルプラットフォームは異なっているのですか?

    • 即時レンダリング(フォワードレンダリング)と遅延レンダリング(ディファードレンダリング)の差です

     

     


    なぜモバイルプラットフォームが異なっているのでしょう?


    デスクトッププラットフォーム

    • 即時モード:グラフィックスコマンドがすぐに実行される
    • GPUと専用ビデオメモリ(> 100 GB /秒)の間で利用可能な帯域幅で膨大な量のデータ転送ができる  電力消費や発熱に厳しい制限がない

    モバイルプラットフォーム

    •  遅延モード:グラフィックスコマンドはドライバによって収集された後で実行される
    • タイルベース:メモリに書き込まれる前に、小さなオンチップバッファでレンダリングされる
    • 帯域幅が大幅に減少し(?5GB /秒)データ転送に大きなパワーを必要とする
    • メモリが統一され、CPUとGPUの間で共有されている

     

      
    ユニティプロ

    ■プロバージョンのみの最適化機能:

        • Profiler (プロファイラ )
        • Level of Detail ( LOD)
        • Occlusion Culling (オクルージョンカリング)
        • Light Probes (ライトプローブ)
        • Static Batching (静的バッチ処理)

     

    OpenGL ES3.0

    ■Unity4.2、Androidの4.3と Mali-T6xxで利用可能
     
    変換フィードバック

    • データ再利用のためのバッファオブジェクトに頂点シェーダー出力を書き込むことができる


    ETC2とEACテクスチャ圧縮フォーマット

    • RGBAのためのサポート(ETC2)および1/2チャンネルのテクスチャ(EAC)

     
    オクルージョンクエリ

    • 可視性を決定するために描かれたサンプルの数を照会できる

    オクルージョンクエリとは: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter06.html
    ジオメトリインスタンシング

    • 同じオブジェクトで独自プロパティを持つ多量のインスタンス

     

    Primitive restart, Uniform Buffer Objects, Pixel Buffer Objects,
           Fences, FP/3D/depth textures, Multiple Render Targets, …

     
     

     


    ボトルネックを特定する

     

    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_12


    CPU

    • ドローコール(描画呼び出し)が多すぎる
    • 複雑なスクリプトや物理演算

    頂点処理

    • 頂点数が多すぎる 
    • 頂点ごとの計算が多すぎる


    フラグメント処理

    • 多すぎるフラグメント処理は、オーバードローにつながる
    • フラグメントごとの多すぎる計算

    ■帯域幅

    • 解像度が大きく非圧縮なテクスチャ
    • 高解像度なフレームバッファ


      
    □CPU バウンド


    ドローコール(描画呼び出し)が多すぎる

    • 静的バッチ(Unityプロのみ)
    • ダイナミックバッチ処理(自動)
    • 錐台カリング(自動)
    • オクルージョンカリング(Unityプロのみ)


    複雑なスクリプト

    • コンポーネントのキャッシング
    • オブジェクトのプール
    • GUIコールの削減
    • OnBecomeVisible()/ OnBecomeInvisible()
    • 時間指定ではなく、フレームごと更新のコルーチン

    複雑な物理計算

    • 一つのメッシュコライダの代わりに複数のコライダーを組み合わせる
    • “Fixed Timestep”の時間間隔を増加(TimeManager)



    頂点バウンド


    ■頂点数の多すぎるジオメトリ

    • 不要な頂点を削除する
    • 不要なハードエッジとUVシームを削除する
    • LODを利用するLODGroupで切り替え(Unityプロのみ)
    • カリング錐台(自動)
    • オクルージョンカリング(Unityプロのみ) 
       

    頂点ごとの計算が多すぎる

    • Unityシェーダのモバイル版を使用


      
    頂点バウンド LOD

    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_15


    ■LODグループを使用することでオブジェクト細部を必要としない時に頂点数を制限することが出来ます(オブジェクトが非常に遠くにあったり 小さかったりする場合)

    • ジオメトリオブジェクトが非常に遠くに表示される場合はビルボード(板ポリゴン)で置き換えることが出来ます

     


    フラグメントバウンド


    ■オーバードロー

    • スクリーンに描画するとき おなじピクセルに何回も書き込みしない
    • オブジェクトを手前から奥に向かって描画するより奥から手前に向かって描画するほうがオーバードローは少ない、デプステストがもたらす恩恵
    • シーン内でのトランスペアレンシー(透過)の量を制限します(パーティクルに注意しましょう)
    • Unityには、ピクセルあたりのオーバードローの量を表示するためにレンダリングモードがある

     

    ■多すぎるフラグメント(ピクセル描画)計算

    • データを出来る限りベイクする(ライトマップ、ライトプローブなど)
    • ピクセル単位のライトの数が含まれている
    • リアルタイムの影を制限(ハイエンドのモバイルデバイスのみ、Unity4プロ)
    • フルスクリーンのポストプロセッシングを避けるようにする
    • Unityシェーダのモバイル版を使用

     

     

    フラグメントバウンド - オーバードロー


    ■レンダリングモードを使用してオーバードローの量を確認する

     

    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_17

     


      

     

    帯域幅バウンド

      ■テクスチャ圧縮の使用

    • OpenGL ES3.0とOpenGL ES2.0、ETC2、EACと、ETC1
    • ASTCはまもなくUnityでサポートされる新しいクロノス規格です

       ■MIPマップの利用

    • 多くのメモリが必要だが画質は向上(より少ないエイリアシングアーティファクト)
    • メモリ帯域幅の最適化(小さいマップからサンプリングしたとき)

       ■ディテールの減少にも対応できる16ビットテクスチャの使用

       ■トライリニアフィルタと異方性フィルタリング の適切な使用 

    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_18

     

     

     


    帯域幅バウンド - ミップマップ

    レンダリングモードを使用してミップマップ・レベルをチェックします

     

    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_19


     

     

     

     

     




    プロファイリングツール

    ユニティ・プロファイラー


    詳細なフレーム単位のパフォーマンスデータを提供します


    プロファイラーは次の特定のデータを提供します

    • CPU使用率
    • レンダリング
    • GPUの使用方法
    • メモリ
    • 物理計算
    • オーディオ

    モバイルデバイス上のコンテンツの実行をプロファイルすることができます

       ■Unityプロでのみ使用可能 


    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_21

     

     

     

     

    ユニティプロファイラ - CPU使用率


    これはレンダリング、スクリプト、物理演算、ガベージコレクションなど におけるCPU使用率を示しています
    どのように時間消費されたかについての詳細な情報を示しています
    スクリプトで発生したすべての関数コールに対しディーププロファイルのデータを追加し有効にすることができます
       ■Profiler.BeginSample()Profiler.EndSample()で特定ブロックにコードを記述することでマニュアルで追跡が可能になります


    Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_22

     

      

     

    ユニティプロファイラ レンダリング


        ■レンダリングサブシステムに関する統計

    • ドローコール
    • 三角メッシュ
    • 頂点
        下部ペインのエディタウィンドウは、レンダリングの統計と同様のデータを示している

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_23

       

       


        
      ユニティプロファイラ メモリ


      使用/確保 されたメモリをより高いレベルで閲覧できます

      • Unityのネイティブの割り当て
      • ガベージコレクトされたMonoが管理する割り当て
      • グラフィックスとオーディオの合計使用されるメモリの推定
      • プロファイラ・データ自身の使用メモリ

      アセット/オブジェクトが使用するメモリも参照できます

      • テクスチャ
      • メッシュ
      • マテリアル
      • アニメーションクリップ
      • オーディオクリップ 
         

      ユニティ4.1以降の 新しい詳細な内訳

       

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_24


        

      ユニティプロファイラ - GPUの使用方法


      CPUのプロファイラと同様の内訳を示しています

      • 不透明なオブジェクトのレンダリング
      • 透明なオブジェクトのレンダリング
      • ディファードシェーディングパス
      • ポストプロセッシング


      モバイル・プラットフォーム上ではまだ利用できません

       

       

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_25

       

       

      ARM Streamlineのパフォーマンスアナライザ

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_26

       

      □サンプルアプリケーションの概要

      7種類のシーンで構成

      • ジオメトリ(高い頂点数、錐台カリング)
      • LOD(LODGroupを通じてLODレベルを使用)
      • パーティクル(透明レンダリング)
      • テクスチャ(ミップマッピングと圧縮)
      • オーバードロー(アルファブレンディングとアルファテスト)
      • ライト(頂点およびピクセルライティング)
      • アトラス(テクスチャアトラス)

      それらのほとんどはプレハブとInstantiate()に基づいています 

      • すべてのインスタンスが最初に一度に作成され、ディアクテイブされる
      • アクティブなインスタンスの数は、目標のFPS値を維持するために適応することができる
      • 位置決めは、スクリーン空間で行われカメラのアスペクト比に基づいている

       

      ジオメトリシーン

      ■高い頂点数のポリゴンオブジェクトがたくさん頂点単位を強調

      • 可視オブジェクトの量または目標のFPS値を設定することができます

       

      • レンダリングに多くの時間が費やされている(緑で描画の部分)
      • 表示オブジェクトの数が増えた部分


        
      カメラを回転し移動することでカリング錐台の動作状態を確認できる(オクルージョンカリングが働いていない)

      • ほとんどのオブジェクトはカメラビューの外に存在して錐台カリングの効果が出ている
      • オブジェクトのほとんどは手前のオブジェクトに隠れているが(処理負荷が上がっている)オクルージョンカリングが有効になっていない
      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_29

       

       

       
      LODシーン 

      3つの異なるレベルのLODGroup

      三角メッシュと頂点がLODレベルで切り替えられ、ドローコールが錐台カリングに影響を受けている


       Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_30 
       


      パーティクルシーン


      すべてのレンダリングは、「透明キュー」で行われます 
      完全なジオメトリバッチがなされている場合(パーティクルエミッタごとに1回のドローコール)


       Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_31 
        
        
        
       

       

      テクスチャシーン

      ■MIPマップを使用した圧縮テクスチャ(右)はメモリ消費が少なく 圧縮されていないもの(左)より良く見える

      右:様々な形式で圧縮+トライリニアフィルタリング+MIPマップ

      左:非圧縮+バイリニアフィルタリング

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_32

       

       

      オーバードローシーン

      ■アルファブレンドされた四角メッシュが透明キューにレンダリングされ オペーク(不透明)キュー内でアルファテストが実行される、両方同じパフォーマンス

       

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_33

       

       

      ライトシーン

      ピクセル単位のライティングがライトごとにドローコールされる(フォワードレンダリング) 

      頂点ごとのライティングはオブジェクト単位のジオメトリパスで実行される ライティング情報は頂点の属性

       

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_34

       

       

      MeshRenderer.Renderは、ms(ミリ秒)あたり5倍のピクセル単位のライティングを実行する(ライトごとに1つのパス)

       

       

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_35


      アトラスシーン

      ■非テクスチャアトラスのレンダリング

      • 各インスタンスは、独自にテクスチャを持っている
      • 同じ材料の複数のインスタンスが同じテクスチャを共有する場合
      • マテリアルごとに一度のドローコール(複数のインスタンスの場合)


      アトラスレンダリングでマテリアルUVを変更

      • 各インスタンスは、同じテクスチャアトラスを共有
      • UV座標はmainTextureScalemainTextureOffsetを使用して変更される
      • 独自のマテリアルがインスタンスに割り付けられるとインスタンス描画の関係は崩れる
      • インスタンスごとに1つのドローコール

       
      アトラスレンダリングでメッシュのUVを変更

      • 各インスタンスは、同じテクスチャアトラスを共有
      • UV座標は、インスタンスのメッシュにアクセスして変更されます
      • 一度のドローコールですべてのインスタンスを描画(マテリアルは一つだけ) 
          

         

      Unite_2013-Optimizing_Unity_Games_for_Mobile_Platforms_37

       

      左から

      :アトラスなしテクスチャ

      60 FPS
      50 draw calls, 374 batched
      21 materials
      :アトラスとマテリアルUV

      40 FPS
      408 draw calls, 0 batched
      395 materials
          マテリアルにバッチが働いていない

      :アトラスとメッシュUV

      60 FPS
      35 draw calls, 374 batched
      21 materials 
          マテリアルにバッチが効いていて、ドローコールが少ない

       

       

       

      :参照リンク

      Practical Guide to Optimization for Mobiles (Unity Manual)
      Optimizing Graphics Performance (Unity Manual)
      Profiler (Unity Manual)


      ShadowGun: Optimizing for Mobile Sample Level (Unity Blog)
      “Fast Mobile Shaders” talk at SIGGRAPH 2011 (Unity Blog)
      Introducing the new Memory Profiler (Unity Blog)
      Unity 4.2 has arrived (Unity Blog)

      http://malideveloper.arm.com/
      OpenGL ES 3.0 Developer Resources (Mali Developer Center)
      ASTC Texture Compression: ARM Pushes the Envelope in Graphics Technology (ARM Blogs)

       

       

       

       

      【編集後記】

       

      ということで久しぶりにブログ更新すると 疲れました えらく疲れました

       

      次回は前回予告したとおりスクリーンスペース系のイメージエフェクト解説をする予定なのですが

      久しぶりに以前書いたコードを見なおしたら キタナクて解説とか出来なそうなのでいまリライトしている最中です

      なので気長に一週間ほど待っていただきたい ほんものの(ry

      いまのところ予定では 次の2種類のエフェクトから徐々に進めていくつもりです

       

      ■SSDO (Screen Space Directional Occlusion)

      スクリーンスペースベースの リアルタイムグローバルイルミネーション

      ■SSLR (Screen Space Local Reflections)

      スクリーンスペースベースの リアルタイム反射モデル

       

       

       

      イメージエフェクト説

       

      この画像のモデルの配置がカオスなところが現在の心境を表しているような気が(´・ω・`)

       

       

       

      ということで

      「この次はモアベターよ」

      こういうネタが通じる年代が見に来ているのか疑問だけれど(小森のおばちゃんネタではないです)

       

      ■その他の方法

       

      ■方法1.

      Data transfer to Vertex Program in Cg- how to pass an Array of floats

      <shader >

      shaderに値を設定:
      myarray0
      myarray1
      myarray2..

       

      <script >
      配列名+数字 を文字列変換してシェーダにセットします:
      for (i=0; i<?; i++) {
      Shader.SetFloat("myarray"+i.ToString(), mycodearray[]);
      }

       

      ”配列名+数字”を文字列に変換して シェーダに転送する方法ですが 配列の要素が少なければこのようなやり方でも良いと思います。

      シェーダ側では switch() ステートメントで切り分ければいいのかな?

      <Example>

      switch(i)
      {
          case 0:
              return myarray0; 
          case 1:
              return myarray1;
          case 2:
              return myarray2;
          default:
      }

       

      ■方法2.

      GL命令を使用した描画はDLLにmaterialIDを渡してC++で処理をすれば、Unityのスクリプトの10倍程度速いという

      stack overflowのスレッドを見かけたことがありました 例によって場所がわからなく。。

      スクリーン全体を描画するような処理の場合はネイティブなDLLプラグインを書くと良いかもしれません。

      今回はシェーダで作業用の配列データを扱う話ですので、そこまで大きなデータは必要なさそうなので割愛させてもらいます。

      興味のある方はリンクを載せておきますので参考にしてみて下さい。

    • Efficiently capturing rendered images- - Unity Answers
    • OpenGL Context from Plugin - Unity Answers
    • qoncept.jp-download-seminar_unity_20110716.pdf
    • Unity - Low-level Native Plugin Interface

       

       

      【後記】

      頂点にデータを付加した方法は初期テーブルとして使用する場合には有効ですが テクスチャを使用した方法に比べると

      シェーダは頂点を一度に1つしか処理できないので、ほかの配列要素にアクセスすることが出来ません たとえばClothのような

      相互に作用する計算をすることができないので、そうした場合はテクスチャにデータを焼きこんだほうが捗る ということです。

      あくまで いまのところこうした方法が良いのではないかという話なのでこの記事にとらわれる必要はありません。

      ゲームエンジンのような頻繁にバージョンが変わるものは、すぐに情報が古くなってしまいますから その時にあわせて

      最適な方法を探っていってください。

       

       

    •  

      マルチUVの検索ワードが多めだったので 今回は予定を変更してUnityのマルチUVと配列の転送についてのおはなしをします。

      コード全体を書いている時間がなかったので解説だけなのですが、記事の作成におもいのほか時間がかかってしまいまして

      コード書いたほうが楽だったじゃないの という企画倒れな面もありましたがなんとかまとめてみました。

       

      1. 頂点データにデータを付加する方法
      2. データをテクスチャに変換する方法
      3. ストラクチャーバッファなどを使用する方法(DX11)※ DX11を使用する方法は以前記事にしましたので参照して下さい
      4. その他

       

       

      現在のところUnityはテクスチャUV0,UV1 2つまでメッシュデータに含むことができますがUV2がライトマップ用に予約されていてそれ以上のマルチUVの値を

      通常では送ることが出来ません。シェーダ配列のサポートは未定ということらしいです フォーラムで最初の要望から数年たってますから あきらめムードのような感じに。。。

       

      ■頂点データに配列データを付加する方法

      Meshの構造体の未使用データ部分にUV値を書き込んでシェーダ側で対応させることで2つ以上のマルチUVを実現することは出来ます。

      もちろんシェーダで使用する様々なデータを付加できますので Mesh.colors(頂点カラー)、Mesh.tangents(タンジェント)あたりが必要なければ

      float4型のデータであればColor型でもVector型でも8888=32ビットのデータとして扱うことが出来ます。

      ただしメッシュデータの最大頂点数は65,536までですので全体データ数はそれを超えることは出来ません。

      もちろん頂点データの構造体をメッシュ表示をさせるかわりにデータテーブルとしてGPGPU計算に使用することも出来ます。

      fig2

       

       

       

      □配列データのエンコード・デコード

      データ長が8ビットにおさまらない場合にはエンコードしてパックする必要があります。 以下にサンプルのコードを載せておきます。

      ■floatをカラーデータ(RGB)にエンコード
      float3 pack(float f) {
            float b = floor(f / (256 * 256));
            float g = floor((f - b * 256 * 256) / 256);
            float r = floor(f % 256);
            return float3(r, g, b); 
      }
      ■カラーデータ(RGB)をfloatにデコード
      float  unpack(float3 color)  { return color.r + color.g * 256 + color.b * 256 * 256; }
      
      ※効率よくデータをパックしたい場合はビット演算子{>> 、<<}をつかいます。boolフラグなど1ビットで表現できるものはデータ量が減らせます。

      以下にエンコードとデコードのサンプルを載せておきます

      http://forum.unity3d.com/threads/95687-Passing-Arrays-to-Shader

      <Example1>

      inline float4 EncodeFloatRGBA( float v )

      {

          float4 kEncodeMul = float4(1.0, 255.0, 65025.0, 160581375.0);

          float kEncodeBit = 1.0/255.0;

          float4 enc = kEncodeMul * v;

          enc = frac (enc);

          enc -= enc.yzww * kEncodeBit;

          return enc;

      }

      inline float DecodeFloatRGBA( float4 enc )

      {

          float4 kDecodeDot = float4(1.0, 1/255.0, 1/65025.0, 1/160581375.0);

          return dot( enc, kDecodeDot );

      }

       

      <Example2>

      // Float to Color in Unity Shader
      float4 F2C(float Value)
      {
      float4 color;
      int c1 = 255;
      int c2 = 255 * 255;
      int c3 = 255 * 255 * 255;
      int full = (int)(Value * c3);
      int B = (full / c2); full -= B * c2;
      int G = (full / c1); full -= G * c1;
      int R = full;
      color = float4(R / 255.0f, G / 255.0f, B / 255.0f, 1.0f);
      return color;
      }


      // Color from RenderTexture to Float
      float C2F(Vector3 Color)
      {
      int c1 = 255;
      int c2 = 255 * 255;
      int c3 = 255 * 255 * 255;
      return (Color.x * 255 + Color.y * 255 * c1 + Color.z * 255 * c2) / c3;
      }

       

       

       

      □テクスチャデータに配列データを変換する方法

      http://forum.unity3d.com/threads/178682-Passing-Arrays-to-shaders

      PassArrayText

      <意訳>

      これは本当に適切であるかどうかは知りませんが、これが配列を渡す簡単な方法です

      1.配列サイズに新しいRenderTextureを設定します。

      2.GL.LoadPixelMatrixを使用して、quadの束(各quad= 1ピクセル)をレンダリングします。

      3.各ピクセルごとにをフロート値をRGBAカラーに符号化します。ルックアップテーブルを作成しておくとより速度があがるかもしれません または別のフレームで事前計算をします。

      camera.renderを呼び出します。

      4.RenderTextureにすべてのクワッドピクセルをblitします 。

      5.RenderTextureをデバイスに転送します。

      デバイスにテクスチャレンダリングするには最良の方法ではありませんが、それは下位互換性を考慮するとsetPixelよりもはるかに高速だと思います。

       

      Unityのフォーラムのほうに配列データを転送するための有効な方法として挙げられていたレスなのですが、いまのところ

      この方法が最も有効そうな感じがしますので 今回はこれを参考に説明してみます。

      ■スクリプト側

      テクスチャデータの配列参照を端的にいうと頂点データにインデックスをつけてテクスチャ化した配列テーブルを参照させる方法です。

      インデックスはさきほどのデータをメッシュデータに追加するやり方でどれか一つデータを利用して頂点番号インデックスを入れ込みます。

      インデックスを登録するデータはUV2でもよいのですがすでに作成されているデータとの兼ね合いも考えて、あまり使用されていない頂点カラーあたりを使用すると良いかも

      しれません。

      インデックスに利用したデータを使用したい場合はテクスチャの方にデータとして焼き込んでそちらからデータを参照すれば良いのでとくにデメリットはないのでは

      1.まず テクスチャからデータを取得するときにUV計算のための頂点データのインデックスの登録をします。

      2バイトのデータ長があれば65,536までの数値が表現できますので頂点データ数はカバーできそうです、ここでは頂点カラーRGBA のうちRGを使用した場合を考えてみます。

      前項でやったように頂点インデックスをエンコードします。

      この方法だとテクスチャメモリが許す限り配列が格納できますので、かなりアドバンテージがあるのではないでしょうか。

       

       

      □RenderTextureの生成とオプション指定

      RenderTextureを作成するときにデータをいれたテクスチャにカラー補間がかからないようにする必要があります。

      次のようにオプションを指定してください

      RenderTexture renderTexture = RenderTexture.GetTemporary( width, height );

      ※以下の2行を追加します

      renderTexture.filterMode = FilterMode.Point;
      renderTexture.wrapMode = TextureWrapMode.Clamp;

      RenderTexture.active = renderTexture;


      □FilterMode

    • Point - テクスチャのピクセル補間をしない
    • Bilinear -バイリニア補間(平均化サンプリング)
    • Trilinear -トライリニア補間 テクスチャサンプル平均とミップマップレベルによるブレンド。

      □TextureWrapMode

    • Repeat -テクスチャタイリング時にタイリングの継ぎ目を補間
    • Clamp ? タイリングの継ぎ目を補間しない.

      FilterModeはPointを選択します 隣接するピクセルでカラー補間をすると値が正確になりません。 
      WrapModeはClampを選択します Wrapはテクスチャタイリング時にタイリングの境目を補完するオプションですがこれも補間なし。

       

       

      □GLを使用したテクスチャへの描画Unity Script Reference-GL.html

      material.SetPass( 0 ); シェーダパスの指定

      GL.PushMatrix(); マトリクスを更新する前に上書きを避けるために現在のカメラのビューマトリクスを退避させます

      GL.LoadPixelMatrix( 0, width, height, 0 );

      使用するマトリクスの指定 LoadOrtho:正射影行列 LoadPixelMatrix:ピクセル行列 LoadProjectionMatrix:プロジェクション行列>

      GL.Begin( GL.QUADS); GLの描画を開始

      for( int i=0; i {

      GL.Color( new Color( □, □, □, □) ); 描画カラーの指定

              GL.Vertex3(□,□,□); ピクセル4角型の頂点を指定する 左まわり座標で記述しないとカメラに向かって裏面になるので描画されない

      GL.Vertex3(□,□,□);

      GL.Vertex3(□,□,□);

      GL.Vertex3(□,□,□);

              }

      GL.End(); GLの描画を終了

      GL.PopMatrix(); 退避させたカメラビューマトリクスを元にもどします

      □RenderTexture から2DTextureへの変換

       

      RenderTexture から2DTextureへの変換でよくサンプルになっている方法にGraphics.Blitを使用する場合と Texture2D.ReadPixels

      使用した場合が挙げられているのですが、ケースによって選択したほうがよさそうなので 2種類のサンプルコードを載せておきます

       

      1.Graphics.Blit を使用する例

      Graphics.DrawTexture on RenderTexture not working...- - Unity Answers

       

      using UnityEngine;

      using System.Collections;

      public class Test : MonoBehaviour

      {

      public Texture2D texture; //初期化テクスチャ.

      public Texture2D stampTexture; //Graphics.DrawtextureでRenderTextureに変換するためのテクスチャ.

      public float posX = 256f; //DrawTexture で使用するテスト用座標.

      public float posY = 256f; //DrawTexture で使用するテスト用座標.

      RenderTexture rt; //RenderTexture を bufferとして定義

      void Start ()

      {

      rt = new RenderTexture (512, 512, 32); // 512x512 pixels サイズのRenderTextureで初期化します

      renderer.material.SetTexture ("_MainTex", rt); //RenderTexure をシェーダのメインテクスチャとして設定

      Graphics.Blit (texture, rt); //Blit命令でtextureをRenderTextureに焼き付けます

      }

      void Update ()

      {

      RenderTexture.active = rt; //RenderTextureを activeに設定  DrawTexture命令が書込できるようにします

      GL.PushMatrix (); //プロジェクション行列とモデルビュー行列をスタック(退避)させます。

      GL.LoadPixelMatrix (0, 512, 512, 0); // ピクセル描画用マトリックスのセットアップ.

      //stampTexture をRenderTexture  座標(posX 、 posY)に配置

      Graphics.DrawTexture (new Rect (posX - stampTexture.width / 2, (rt.height - posY) - stampTexture.height / 2, stampTexture.width, stampTexture.height), stampTexture);

      GL.PopMatrix (); //プロジェクション行列とモデルビュー行列をスタックから呼び戻します。

      RenderTexture.active = null; //RenderTextureを描画できないように.

      }

      }


       

      メインカメラでレンダリング作業をしてはいけませんのでレンダーテクスチャ用のメインカメラのクローンを作成します。

      以下のコードを参考にしてください

      if (!shaderCamera)

      {

                  shaderCamera = new GameObject("ShaderCamera", typeof(Camera));

                  shaderCamera.camera.enabled = false;

                  shaderCamera.hideFlags = HideFlags.HideAndDontSave;

      }

              Camera cam = shaderCamera.camera;

              cam.CopyFrom(camera); //   MainCameraからプロパティコピー

              cam.backgroundColor = new Color(0, 0, 0, 0);

              cam.clearFlags = CameraClearFlags.SolidColor;       

              cam.targetTexture = buffer; // buffer=RenderTextureの指定

            

         

       

    •  

       

       

      2.Texture2D.ReadPixels を使用する例

       

    • ”GL2TextureExample2.cs” Rendering GL to a Texture2D immediately in Unity4

      を参考にTexture2D.ReadPixels を使用した場合を考えます。

    • // Initialize and render

      RenderTexture rt = new RenderTexture(width, height, 24);

      activeCamera.targetTexture = rt;

      activeCamera.Render();

      RenderTexture.active = rt;

      Texture2D newTexture = new Texture2D( width, height );

      newTexture.ReadPixels( new Rect( 0, 0, width, height ), 0, 0 );

      bool applyMipsmaps = false;

              newTexture.Apply( applyMipsmaps );

      bool highQuality = true;

              newTexture.Compress( highQuality );

      // Clean up

      activeCamera.targetTexture = null;

      RenderTexture.active = null; // RenderTextreをオフにする

      DestroyImmediate(rt);

       

      □Texture2D.ReadPixels(Rect( 0, 0, width, height ), 配置ピクセル座標x, y、ミップマップの再計算(bool値))

      スクリーンからテクスチャにキャプチャする命令 ミップマップ再計算フラグは無視してもよいです。

      □Texture2D.Apply (ミップマップの生成を行うかの指定(bool値)、テクスチャをGPUにつづけて転送しない(bool値))

      最後に呼び出してGPU側にテクスチャを更新 ミップマップの生成をfalseにすれば省メモリになります 変換速度もあがる はず。

      テクスチャの解像度を階乗 8,16・・・256・・・と指定しないとGPUに転送したときテクスチャサイズが変換されてから再転送されるため遅延が発生します。

      ハードウェアとライブラリのバージョンによるため必ず起きるものではなく バージョンの高いライブラリでは問題ないとか。。

      矩形テクスチャで縦横比が違う場合も古いライブラリでは1:1に変換する手順が入るばあいがあり

      Texture2D.Compress(highQuality: bool値)

      テクスチャ圧縮オプション。 highQualityをセットすると圧縮がかからないためテクスチャの変換速度が向上します。

      ※テクスチャの圧縮はハードウェアでサポートされていない場合は無視されます。

       

       

      ■ シェーダ側

      ■テクスチャフェッチ (テクスチャ参照)

      http://forum.unity3d.com/threads/63231-How-to-use-vertex-texture-fetch

      Shader "tex2Dlod" {

          Properties {

              _MainTex ("Base (RGB)", 2D) = "white" {}

          }

          SubShader {

              Tags { "RenderType"="Opaque" }

              LOD 200

             

              CGPROGRAM

              #pragma target 3.0

              #pragma surface surf BlinnPhong vertex:vert

              #pragma glsl

              struct Input {

                  float2 uv_MainTex;

              };

       

              void vert (inout appdata_full v) {

                  #if !defined(SHADER_API_OPENGL)

                  float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));

                  v.vertex.y += tex.r * 2.0;

                  #endif

              }         

              ENDCG

          }

          FallBack "Diffuse"

      }

       

      tex2Dlod (_MainTex, float4( (float2)テクスチャUV,0,LOD値<0~7 0で最大解像度> ));

      以前にも説明しましたが頂点シェーダ(vertexシェーダ)でテクスチャフェッチする場合には頂点データの計算がされていないため

      LOD値が確定していませんそのため tex2Dlodを使用して強制的にLOD値を割り付ける必要があります。

      ピクセルシェーダ(fragmentシェーダ)ではふつうにtex2d()を使用すればいいです。

        tex2Dlod はglの命令なので #pragma glsl を記述するかまたは以下のように書いてください

       

            #if !defined(SHADER_API_OPENGL)

               float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));

               v.vertex.y += tex.r * 2.0;

             #endif

       

       

      □UV値の計算

      uv

       

      UV値は1をテクスチャサイズで割って求めます V方向(uv.y)は0.5を掛けてテクスチャの中央あたりをサンプルするように

      このサンプルではテクスチャサイズが u方向に配列サイズ分のピクセル数 v方向に1ピクセルのテクスチャをイメージしています。

       

      uint   idx = color.r*255+color.g*255*255; //頂点インデックスのデコード

      float2 uv = 1.0 / TextureSize;
      uv.y *= 0.5;
      float4 texCoord = float4( ( float )
      idx * uv.x, uv.y, 0, 1 );

       

      ※DX10以降(シェーダモデル4)では SV_で始まるシステム値セマンティックスを使用することができて (uint)SV_VertexID

      Mesh頂点の順番に対応しているようなので頂点シェーダ内でUV値の計算をすることが出来るようです。

      セマンティクス (DirectX HLSL)

      <Example>

      vert2f vert(appdata_full v , uint id : SV_VertexID)
                  {
                      vert2f o;

      float2 uv = 1.0 / TextureSize;
      uv.y *= 0.5;

      float4 texCoord = float4((float) id * uv.x, uv.y, 0, 1 );

             赤字の部分がuint型の頂点インデックスIDで代用
                  }  

       

      GLでサポートされていれば便利そうなのですが シェーダの後方互換を保つためであれば、いまはあまり使わないほうがいいのかもとも思います。

       

      ちなみに SV_で始まるシステム値セマンティクス「SV_POSITION は POSITION と同じ値が入っているのはなぜ ていうかSV_って何?」という質問に

      ユニテイの開発者Aras氏「よくわからないけど DX11以降と互換を取るためじゃないかな?」 ということで皆よくはわかってないらしい。ちょっと安心?

       

       

       

      □テクスチャフェッチでループを使用する時の注意

      テクスチャフェッチにループを使用する場合HLSLではfor ステートメントを記述する場合コンパイル方法を制御するパラメーターとして

      [loop]または[unroll]を指定することが出来ます。

      Unityのシェーダで予約語あつかいで記述出来ないようですが シェーダを移植する際などに留意する点として書いておきます。<コンピュートシェーダでは有効みたい>

      ※Unityではコンパイルが自動最適化されているため[loop][unroll]2通りの最適化コンパイルをして実行速度が速いほうを選択しているそうです。

       

      [unroll]

      for ( i=1;i{

              Statement Block;

      }

      forStatement

       

      この2つのオプションはコンパイル後のコード記述のされかたが違います.

      [loop]指定時はコンパイル後にアセンブラコードでループがそのまま記述されます メモリサイズは抑えられますが実行速度はやや低速となります。

      [unroll]指定時はループ内のコードがループ回数分記述されます メモリサイズは大きくなりますが 実行速度は高速になります。

      このオプションは通常は省略可能ですがループ内でテクスチャフェッチ(テクスチャを参照)する場合は[unroll]を強制すると動作が安定するということです

      ちなみにHLSLシェーダでは、ほぼC言語の命令セットとおなじ命令がサポートされているため ループにwhileステートメントも使用できますが

      この場合シェーダが最適化されませんので実行速度は低下する場合があります。

      シェーダの実行速度は条件によるのですが、 最適化された計算時間 [unroll]指定時がPCが100として [loop]ではモバイルで200 xbox360で160 程度に遅延

      という参考数値の記述がありました。

      シェーダ内でループをするときには、なるべくforステートメントを使用するほうが良いかもしれません。

       

       

      □テクスチャ化したデータの書き出し

      一度データ化したデータはテクスチャリソースとして書き出してしまえばデータ生成の初期化手順を省けますので データがだいたいフィックスした段階で

      アセットデータから読み出すかたちでシェーダにセットすると良いかと思います。

      以下のUnityコミュニティWikiのスクリプトを参考にしてみてください。※Application.CaptureScreenshot を使うやりかたもあるとかないとか

      Unity Script Reference-Texture2D.EncodeToPNG.html

      ExportNormalmap - Unify Community Wiki

      □ExportNormalmap.js

      @MenuItem ("Assets/Export Normalmap...")  
       static function ExportNormalmap () {
      	var tex = Selection.activeObject as Texture2D;
      	if (tex == null) {
      		EditorUtility.DisplayDialog("No texture selected", "Please select a texture.", "Cancel");
      		return;
      	}   // Force the texture to be readable so that we can access its pixels
      	var texPath = AssetDatabase.GetAssetPath(tex);
      	var texImport : TextureImporter = AssetImporter.GetAtPath(texPath);
      	if (!texImport.isReadable) {
      		texImport.isReadable = true;
      		AssetDatabase.ImportAsset(texPath, ImportAssetOptions.ForceUpdate);
      	}   var bytes = tex.EncodeToPNG();
      	var path = EditorUtility.SaveFilePanel("Save Texture", "", tex.name+"_normal.png", "png");
      	if (path != "") {
      		System.IO.File.WriteAllBytes(path, bytes);
      		AssetDatabase.Refresh(); // In case it was saved to the Assets folder
      	}
      }





      ■追記 2014 12.18

      文字数制限で入らなかった部分を同時期にアップしたのですが、あまり閲覧されていないようなのでリンクしておきます。

      うにばな(シェーダへ配列データの転送 マルチUVなど) その2

      うにばな(シェーダへ配列データの転送 マルチUVなど) その3

      うにばな (シェーダへ配列データの転送) その4 JsonでMayaからUnityへ

      ※170607 補足記事をアップしました うにばな スクリーンスペース系のシェーダに関するTips

       

       

      今回は”エフェクト用のシェーダに関するアイデア”の補足でGrabTextureを使用する場合のパーティクルのカラー合成をしてみます。

      GrabTextureは背景からテクスチャにキャプチャした画像をカラー合成の対象にできる命令で スクリーンサイズのスナップショットをテクスチャに展開します。

      GrabTexture命令で 取り込んだテクスチャにシェーダでモデルのカラーを合成をしてやれば ”エフェクト用のシェーダに関するアイデア”で使用したシェーダと同じような効果がねらえます。カラーを合成する式は

      これも以前掲載した フォトショップの合成式を使用することが出来るため フォトショップ上の見た目と同等の表現が期待出来て デザイナーにとっては非常にありがたい機能です。 ただしGrabTexture命令は処理負荷が高く現在のモバイルやスマホで動作させるには重ためなので気をつけてください。

       

      【fig1】

      grabtest

       

       

      今回作成したサンプルシェーダコードは以下の通りです

      ■ 『BlendTest(GrabTex) . shader" 』

      Shader "Custom/Particles/BlendTest(GrabTex)" {

          Properties {

              _TintColor ( "Tint Color", Color ) = ( 0.5, 0.5, 0.5, 0.5 )
              _MainTex ( "Particle Texture", 2D ) = "white" {}
        
          }

          Category {

              Tags { "Queue"="Transparent" "RenderType"="Opaque" }
             
                Blend One OneMinusSrcAlpha
               Alphatest Greater 0.1
               ZWrite On
              
              SubShader {

                  GrabPass { 
                                                       
                      Name "BASE"
                      Tags { "LightMode" = "Always" }
        
                  }


                  Pass {
                      Name "BASE"
                      Tags { "LightMode" = "Always" }

                      CGPROGRAM
                      #pragma vertex vert
                      #pragma fragment frag
                      #pragma multi_compile_particles
                      #include "UnityCG.cginc"
                      #pragma target 3.0

                      struct appdata_t {
                          float4 vertex : POSITION;
                          float2 texcoord: TEXCOORD0;
                      };

                      struct v2f {
                          float4 vertex : POSITION;
                          float4 uvgrab : TEXCOORD0;     
                          float2 uvmain : TEXCOORD2;
                      };
         
                      sampler2D _GrabTexture;
                      float4 _GrabTexture_TexelSize;

                      sampler2D _MainTex;
                      float4 _MainTex_ST;
                     
                      fixed4 _TintColor;
                    

                      v2f vert ( appdata_t v ) {

                          v2f o;

                          o.vertex = mul( UNITY_MATRIX_MVP, v.vertex );
                          #if UNITY_UV_STARTS_AT_TOP
                          float scale = -1.0;
                          #else
                          float scale = 1.0;
                          #endif
                          o.uvgrab.xy = ( float2( o.vertex.x, ( o.vertex.y * scale ) ) + o.vertex.w ) * 0.5;
                          o.uvgrab.zw = o.vertex.zw;           
                          o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );

                          return o;

                      }


                      half4 frag( v2f i ) : COLOR {
                  
                         half4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD( i.uvgrab ) );
                         
                         col.a = 0;
                       
                         half4 base = tex2D( _MainTex, i.uvmain);      
                        // このようなブレンド式を使用してもよいです
                       //  return float4( (base.rgb < 0.5 ? (2.0 * base.rgb * col.rgb) : (1.0 - 2.0 * (1.0 - base.rgb) * (1.0 - col).rgb)).rgb , base.a);

                          return float4(base.rgb + _TintColor * pow(base.rgb,2.0 )* col.rgb , base.a);
                  

                      }
                      ENDCG
                  }
                 
                
              }

          }

      }

       

      ■ GrabTextureの取得は GrabPassをk術するだけです samplerの指定とセットなので記述漏れしないようにしてください

      GrabPass
                                                       
                      Name "BASE"
                      Tags { "LightMode" = "Always" }
        
                  }

      ・・・・・・・・・・・・・・・・・・・・・・・

      sampler2D _GrabTexture;

      ・・・・・・・・・・・・・・・・・・・・・・・

      o.uvgrab.xy = ( float2( o.vertex.x, ( o.vertex.y * scale ) ) + o.vertex.w ) * 0.5;
      o.uvgrab.zw = o.vertex.zw;
            

       

      half4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD( i.uvgrab ) );

      Unityのマクロ関数 UNITY_PROJ_COORD にワールド頂点座標を与えることによってカメラのプロジェクション方向から見たスクリーン座標のUV値に変換されます。

      Tags { "Queue"="Transparent" "RenderType"="Opaque" } タグはレンダーキュー(Queue)に透明(Transparent)が指定してあればアルファを考慮したソートが行われるため RenderTypeは無視しても構いません。

      ■Blend One OneMinusSrcAlpha は <背景 1 :前景 1-alpha値> で合成を指定します。アルファ抜きの場合はこれを使用。

      ZWriteをOnにするのを忘れないように depthバッファの書き込みが行われないと正確なソートが行われません。

      Alphatest Greater 0.1 はしきい値(例の場合0.1)を境界にして透明部分の抜け具合を決定します。


      Blend One OneMinusSrcAlpha
              Alphatest Greater 0.1
               ZWrite On


       

      ■ライブラリの種類によっては取り込んだテクスチャのUV座標のV方向(スクリーン座標のY軸方向)が上下逆になることがあるので

      UNITY_UV_STARTS_AT_TOP の変数を監視してテクスチャUVのV方向に−1を掛けてフリップする必要があります


      #if UNITY_UV_STARTS_AT_TOP
                          float scale = -1.0;
                          #else
                          float scale = 1.0;
                          #endif

       

      ■カラー合成式に関してはどのような書き方してももちろん構いません 見た目でより良い結果になるようにしてください。

       

      フォトショップのブレンド式 を使用する場合   

      return float4( ( base.rgb < 0.5 ? (2.0 * base.rgb * col.rgb) : (1.0 - 2.0 * (1.0 - base.rgb) * (1.0 - col.rgb).rgb)).rgb , base.a );
                    

      ● 加算と乗算を組み合わせる場合

      return float4( base.rgb + _TintColor * pow(base.rgb,2.0 )* col.rgb , base.a );

      テクスチャはアルファ抜きしたものを使用しますが、 カラーの明度からアルファマスクを計算して取得することも出来ます

      【Example】

      float alpha =Luminance(color.rgb) ;

      たとえばUnityのマクロ関数Luminanceを使用します RGB値カラーを輝度値 に変換する関数で "UnityCG.cginc"ファイルに記述されています

      Luminance( ) の代入はfloat3型ですので float4型のカラーを渡すときは 「color.rgb」というようにSwizzleで記述します。

      inline fixed Luminance( fixed3 c )
      {
          return dot( c, fixed3(0.22, 0.707, 0.071) );
      }

       


      【追記】

      リファレンスには下のように GrabPassにオプションパラメータとしてテクスチャのスケールが指定できると書いてありますが コンパイルが通ったことがありません Unity公式フォーラムでもバグ扱いされていてだれも使い方をわかってないようなんですが、どうやって使用するんでしょう? ご存じの方がいればコメント頂きたいのです。

      GrabPass {

          TextureScale 0.5 

         TextureSize 256

          BorderScale 0.3

      // following are regular pass commands

          Tags { "LightMode" = "VertexLit" }

          Name "BASE"

      }

       

      今回は以上です 次回はなるべくはために更新するつもりですが 少しレベルを上げるかもしれませんので頑張って読解してみてください。

      質問がある方はコメント欄にどうぞ

       

      それではまた

      ↑このページのトップヘ