Yaminabe

らくがきぶろぐ

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

カテゴリ: シリーズ講座

 

 

 

こんばんわです

 

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

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

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

 

そんな業務連絡

 

 

 

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

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

 

今回は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"

    }

     

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

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

     

    それではまた

     

     

    drawMeshTest

    ■webプレイヤー

    https://dl.dropboxusercontent.com/u/18978109/DrawMeshTest/DrawMeshTest.html

     

    Unity公式のフォーラムで大量のメッシュを描画する方法として有効だというはなしのGraphics.DrawMesh命令を簡単に解説します

    Unityの新しいバージョンでプロパティブロックまわりが改良されたそうなので プロパティブロックを使用してサンプルを作成してみました

    < どこが新規に変わったのかいまいちわからないのですが >

     

    ちなみに情報としては古めなので すでにほかのところに詳しい記事があるかもしれませんが、ここの管理人は怠けものなので国内の記事はあまり見ていません。







    ■Graphics.DrawMesh

    ● static function DrawMesh(mesh: Mesh, position: Vector3, rotation: Quaternion, material: Material, layer: int, camera: Camera = null, submeshIndex: int = 0, properties: MaterialPropertyBlock = null): void;

    ● static function DrawMesh(mesh: Mesh, matrix: Matrix4x4, material: Material, layer: int, camera: Camera = null, submeshIndex: int = 0, properties: MaterialPropertyBlock = null): void;

     

    【パラメータ】

    mesh描画するメッシュデータ

    positionメッシュの位置。

    rotation      メッシュの回転。

    matrixメッシュの変換行列(位置、回転、その他の変換)。

    materialマテリアルを指定する。

    layerレイヤを使用する。

    camera    :デフォルトはNULL。メッシュはすべてのカメラで描画されますが 指定がある場合指定されたカメラでレンダリングします。

    submeshIndex描画するメッシュのどのサブセット。これは、複数の材料で構成されたメッシュに適用される。

    propertiesメッシュが描画される直前にマテリアルにプロパティ変更を適用します。※MaterialPropertyBlock

     

    DrawMesh命令は1フレームごとにメッシュを描画します。通常のゲームオブジェクトと同様にシェーディングされ影が落ちてプロジェクターの影響を受けることができます。デフォルト状態ではすべてのカメラにレンダリングされますが特定のカメラを指定することもできます。

    ゲームオブジェクトの作成と管理のオーバーヘッドを避けて大量のメッシュを描画したい場合DrawMesh命令は有効に機能します。DrawMeshはすぐにメッシュを描画せず Updateの後にレンダリングパイプラインにデータを送り、そのあと通常のレンダリングプロセスの一部としてレンダリングされます。 すぐにメッシュを描画したい場合は、Graphics.DrawMeshNowを使用します。

    DrawMeshはすぐにメッシュの描画がされないため、関数の呼び出しの時に材料特性を変更すると、メッシュはそれらをピックアップすることはありません。あなたは、同じマテリアルを用いてメッシュのシリーズを描きたいのですが、すこしだけマテリアルのプロパティ(例えば色を変更)をする場合は、MaterialPropertyBlockのパラメータを使用します。

     





    ■MaterialPropertyBlock

    MaterialPropertyBlockGraphics.DrawMeshによって使用されているRenderer.SetPropertyBlockです。、同じマテリアルで複数のオブジェクトを描画するような場合、たとえばメッシュの色を変更したい場合シェーダ内のプロパティの値を部分的に変更することで対応します。

     

    【機能】

    AddColor            : カラープロパティを追加します。

    AddFloat             :フロート型プロパティを追加します。

    AddMatrix         : マトリックス型プロパティを追加します。

    AddTexture       :テクスチャ型プロパティを追加します。

    AddVector         :ベクター型プロパティを追加します。

    Clear                  : プロパティ値のクリア。

    GetFloat             : プロパティブロックからフロートを取得します。

    GetMatrix          : プロパティブロックから行列を取得します。

    GetTexture        : プロパティブロックからテクスチャを取得します。

    GetVector          : プロパティブロックからベクトルを取得します。

     

     

    【Example】

    public class  XXXXX : MonoBehaviour
    {

         private MaterialPropertyBlock _PropertyBlock; 
         private int _ColorPropertyId ;

     

    void Start()
    {

                   _PropertyBlock    = new MaterialPropertyBlock();
                   _ColorPropertyId = Shader.PropertyToID("_Color");

    }

    void Update() 
    {

                   _PropertyBlock.Clear(); 
                   _PropertyBlock.AddColor(_ColorPropertyId, Color.white);    
                
      Graphics.DrawMesh( Mesh, Position , Rotation, Material, 0, MainCamera, 0,_PropertyBlock);

      }

    }

     

    PropertyBlock を効率よく使用するためには1つのブロックを作成してDrawMeshをコールするたびにブロックを再利用する方法です。

    Graphics.DrawMesh()の前に使用ブロックをクリアし AddXXX を使用して値を追加します。

    指定できるプロパティは使用しているシェーダ内で登録されているプロパティとなります。 複数のシェーダを使用する場合はShader.Find(シェーダ名)で書き換え対象のシェーダを指定する必要があります。

     

     

    ●DrawMeshManager.cs

    using System.Collections.Generic;
    using UnityEngine;
    using Assets;

    public class DrawMeshManager : MonoBehaviour
    {
       
        public Mesh ObjMesh;
        public Material Material1;
       
        public Camera MainCamera;
        public Transform Player;
        public float ForceDrawWithinDistance = 50.0f;
       
        public int Maxval = 100;
        public float  Scaler = 2.0f;   
       
        private List Objs = new List();
       
        private MaterialPropertyBlock _PropertyBlock;
        private int _ColorPropertyId ;
       
       
        private void Start()
        {
           
            MainCamera = Camera.main;                
            float Offset=(Maxval*Scaler/2.0f)-Maxval*Scaler;
            int ObjectType = 0;
           
            for(var x=0 ; x        {                
                ObjectType = x%2;          
                for(var y=0 ; y            {                   
                    for(var z=0 ; z                {    
                                                                    
                        ObjData newObj = new ObjData( ObjectType,
                                                new Vector3((float)x*Scaler+Offset,(float)y*Scaler+Offset,(float)z*Scaler+Offset),
                                                Quaternion.Euler(0, 0 ,0));

                        Objs.Add(newObj);                                       
            
                    }
               }
               
            }
           
            _PropertyBlock = new MaterialPropertyBlock();
            _ColorPropertyId = Shader.PropertyToID("_Color");       
            
        }

       
        private void Update()
        {
           

            Vector3 playerPosition = Player.position;
            var FrustumPlanes = GeometryUtility.CalculateFrustumPlanes(MainCamera);

            
            float _timer = Time.timeSinceLevelLoad;               
            var ObjBounds = ObjMesh.bounds;
           
            foreach (var Obj in Objs)
            {
                            
            //    int ObjType   = Obj.ObjType; // メッシュオブジェクトを複数定義する場合に使用する
                
                    ObjBounds.center = Obj.WorldPosition;

           
                float distance = Vector3.Distance(Obj.WorldPosition, playerPosition);

           
                if (distance < ForceDrawWithinDistance || GeometryUtility.TestPlanesAABB(FrustumPlanes, ObjBounds))
                {
                       
                    float ofst = Mathf.Sin((Obj.WorldPosition.y + _timer)/4.0f)*1.5f;
                                    
                    _PropertyBlock.Clear();
                    _PropertyBlock.AddColor(_ColorPropertyId, Obj.Color);   
                    Graphics.DrawMesh(ObjMesh, Obj.WorldPosition + new Vector3(ofst,0,ofst) , Obj.Rotation, Material1, 0, MainCamera, 0,_PropertyBlock);
                                              
                        
                }
            }
        }
    }

    ●ObjData.cs 【構造体クラス】

    using System;
    using UnityEngine;


    namespace Assets
    {
        public class ObjData
        {
            private  int _ObjType;
            private  Vector3 _WorldPosition;
            private  Quaternion _Rotation;
            private  Color _Color;
           

            public ObjData(int ObjType, Vector3 worldPosition, Quaternion rotation)
            {
                _ObjType = ObjType;
                _WorldPosition = worldPosition;
                _Rotation = rotation;
               
                if (ObjType == 0)
                {
                   
                    _Color = Color.white;
                   
                }
                else
                {

                   _Color = new Color(1.0f,0.2f,0.05f);
                   
                }      
                
            }

            public Vector3 WorldPosition
            {
                get { return _WorldPosition; }
            }
            public Quaternion Rotation
            {
                get { return _Rotation; }
            }
            public int ObjType
            {
                get { return _ObjType; }
            }       
            public Color Color
            {
                get { return _Color; }
            }
           
        }
    }

    いままでは配列で管理して結合した後GPUに転送という方法が知られていましたが それにに比べると メモリ消費も少なくて なにより頭を悩ませることがないのがいいですね。

    画像のStatisticsで表示されるDrawCall数は3000を越えていますが FPS値はふつうに高いままなので Unityの中身はわからないので想像ですが、おそらくCPU側でGraphics.Draw関数をコールした回数だと思われます。VRAMへの描画は1フレームで行われているらしいです。

    左のGUIのFPS表示はエディタから実行した数値です。 PCの環境にもよりますがwebプレイヤーでは60フレーム程度はでていると思います。

    Unityのツリーシステムなどでは メモリ使用量はモデルの頂点数分の合計となるためかなり大きめな数値となっています おそらくは内部でメッシュをマージして転送する方法を行っているのだと思いますが、今回のDrawMesh関数を使用するサンプルはアップデート内部でシェーダにモデル1つ分のデータを送るだけなのでGPU側のメモリはほとんど消費されていません

    それからスキンモデルにもDrawMwshを使用してみたいところなのですが現時点ではスキンウェイトのモデルはダイナミックバッチがかからない仕様になっているそうなのです

    スキニングのフィルターがブラックボックスなので調べてみないとわかりませんが、アニメーションするキャラクターを大量に配置したい場合はスキンウェイト用のシェーダを自作する必要があるかもしれません。

    ↑このページのトップヘ