Version: 2017.1
スクリプトとゲームプレイ方法
スクリプトの最適化

レンダリングの最適化

このセクションでは、最適化のレンダリングを技術的に紹介します。パフォーマンス向上のためのライティング結果を焼く方法と Shadowgun の開発者が彼らのゲームを素晴らしいものに見えるようにするライティングをベイクして高コントラストのテクスチャをレバレッジする方法を示します。モバイルに最適化されたゲームがどのように見えるか、一般的な情報を探している場合は、グラフィックスメソッドページをチェックしてください。

アーティストらしく!

時には、あなたのゲーム内のレンダリングを最適化することは、いくつかの割に合わない仕事を必要とします。Unity が提供するストラクチャーのすべては何かを速く動作させるのが容易になりますが、限られたハードウェア上で忠実に動作する必要がある場合、あなたが、自分自身でもっとより速くする鍵となるストラクチャーの変化を導入することができ、ストラクチャーを回避して行く方法でより高速になものになります。選択するあなたのツールは、エディターのスクリプト、シンプルなシェーダーと昔ながらのアート制作です。

フードに下に潜る方法

まず最初に、この マテリアルとシェーダーとテクスチャをチェックしてください。

  • ビルドインシェーダー
    • シェーダーにビルドされたソースコードを調べます。多くの場合、何か違う新しいシェーダーを作りたい場合は、2 つのすでにあるシェーダーの部品を取り、それらを一緒に置くことで達成できます。
  • サーフェースシェーダーのデバッグ (#pragma debug)
    • CG シェーダーは、すべてのサーフェスシェーダーから生成した後、そこで完全にコンパイルされます。あなたのサーフェスシェーダーの先頭に #pragma DEBUG を追加した場合は、インスペクター経由でコンパイルされたシェーダーを開くと、中間 CG コードを見ることができます。これは、シェーダーの特定の部分が実際にはどのように計算されるか、検査するのに便利で、また、あなたがサーフェスシェーダーから欲しい特定の側面をつかむためや CG シェーダーに適用するのに使えます。
  • Shader Include ファイル
    • シェーダーヘルパーコードの多くは、すべてのシェーダーに含まれており、通常は使用されていません。しかし、これが、シェーダーがどこにも定義されていないような WorldReflectionVector などの関数を呼び出すのを目にする理由なのです。Unity はこれらのヘルパーの定義が含まれているいくつかの組み込みのシェーダーインクルードファイルを持っています。特定の機能を検索するには、異なるインクルードファイルすべてを検索する必要があります。
    • これらのファイルは、Unity がシェーダーを簡単に書けるように使用する内部構造の主な部分です。ファイルは、リアルタイムの影、異なる光の種類、ライトマップ、複数プラットフォームのサポートのようなものを提供します。
  • ハードウェアのドキュメント

徹底的に Shadowgun

Shadowgun は、それを実行するハードウェアを考慮した素晴らしいグラフィカルな成果です。アートの質はパズルの鍵であると言われますが、そのような品質を達成するために、プログラマは彼らのアーティストの可能性を最大限引き出させるカップルのトリックがあります。

グラフィックスメソッドページでは、すばらしい最適化の例として Shadowgun の黄金の彫像を使用してます:それらの彫像に通常のマップを使用していくつかの固体を定義を与える代わりに彼らはテクスチャにライティングのディテールをベイクしました。ここに、あなたがあなた自身のゲームで同様のテクニックをどのように、なぜ、使用するのか表示しています。

リアルタイムのシェーダーコード vs ベイクされた黄金の彫像

// これは、静的にライトマップしたジオメトリ上に法線マップした
// スペキュラーハイライトを描画するピクセルシェーダーコードです。

// 5 テクスチャの読み込み, 多数インストラクションあり

SurfaceOutput o;

fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
fixed4 c = tex * _Color;
o.Albedo = c.rgb;

o.Gloss = tex.a;
o.Specular = _Shininess;

o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));

float3 worldRefl = WorldReflectionVector (IN, o.Normal);
fixed4 reflcol = texCUBE (_Cube, worldRefl);
reflcol *= tex.a;
o.Emission = reflcol.rgb * _ReflectColor.rgb;
o.Alpha = reflcol.a * _ReflectColor.a;

fixed atten = LIGHT_ATTENUATION(IN);
fixed4 c = 0;

half3 specColor;
fixed4 lmtex = tex2D(unity_Lightmap, IN.lmap.xy);
fixed4 lmIndTex = tex2D(unity_LightmapInd, IN.lmap.xy);

const float3x3 unity_DirBasis = float3x3( 
float3( 0.81649658,  0.0, 0.57735028),
float3(-0.40824830,  0.70710679, 0.57735027),
float3(-0.40824829, -0.70710678, 0.57735026) );

half3 lm = DecodeLightmap (lmtex);

half3 scalePerBasisVector = DecodeLightmap (lmIndTex);

half3 normalInRnmBasis = saturate (mul (unity_DirBasis, o.Normal));
lm *= dot (normalInRnmBasis, scalePerBasisVector);

return half4(lm, 1);
// テクスチャにベイクされたライティングの
// ピクセルシェーダーコード

// 2 テクスチャの読み込み, インストラクションはほとんどなし

fixed4 c = tex2D (_MainTex, i.uv.xy);   

c.xyz += texCUBE(_EnvTex,i.refl) * _ReflectionColor * c.a;

return c;

反射衝突スペキュラ 反射光レンダリング(ベイク)

テクセルへのレンダリング

リアルタイムのライティングは間違いなく高品質ですが、ベイクした方が、パフォーマンスはかなり向上します。では、どうやってベイクするのでしょう? Render to Texel というエディターツールが、この目的のために作成されました。このツールは、以下の工程を経てテクスチャにライティングを焼き付けます:

  • スクリプトを経由して、ワールド空間の接線空間の法線マップを変換
  • スクリプト経由でワールド空間位置マップを作成
  • 光ごとに 1 つの追加のパスを持つ 2 つのプレビューマップを使用して、テクスチャ全体のフルスクリーンパスをテクスチャにレンダリング
  • あなたのゲームにおいて、いくつかの異なる見晴らしポイントからの平均の結果は、一般の視野角から至る所、または、少なくとももっともらしく見える何かを与えられます。

これが最高のグラフィックの最適化がどのように機能するかです。これらはエディターでそれらを予備成形し、ゲームを実行する前に計算の爆発を回避します。一般的に、これがあなたがしたい何かです:

  • すばらしく見える何かを作成し、パフォーマンスを心配しないでください。
  • Unity の lightmapper やテクセルのレンダリングやエディターの拡張機能の様なツール、スプライトパッカーが何かにそれをベイクする等を使用し、レンダリングすることは非常に簡単です。
    • 独自のツールを作ることは、これを行うための最善の方法で、なんらかの問題があなたのゲームに提示されたらあなたに適した最適なツールを作成することができます。
  • “輝き”のいくつかを並べ替えするためにあなたがベイクした出力を修正するシェーダーとスクリプトを作成します;目を引く効果は、動的な光の幻影を生成します。

光の周波のコンセプト

ちょうど、オーディオトラックの低音と高音のように、画像はまた、高周波と低周波成分を有し、レンダリングするとき、さまざまな方法で処理するのが最善で、完全なステレオサウンドを作成するためにサブウーファーとツイーターを使用する方法に似ています。画像の異なる周波数を可視化する 1 つの方法は、Photoshop で“ハイパス”フィルターを使用することです。Filters->Other->High Pass . あなたが以前にオーディオの作業を行っている場合は、あなたはハイパスの意味がわかるでしょう。本質的に、すべての周波数のうち、X よりも低いと遮断され、通過させるパラメーターをフィルターに渡します。画像の場合、ガウスぼかし、低域を通過するのと同じです。

周波数は物事を分離することと、何か処理方法を決定するよい方法であるため、リアルタイムグラフィックスに適用されます。例えば、基本的なライトマップ環境では、最終画像は低い周波数であるライトマップと高周波であるテクスチャの合成によって得られます。Shadowgun では、低周波数の光は、迅速に光プローブを用いて文字に適用され、高周波光は、任意の光の方向を持つ単純なバンプマップシェーダーを使用することによって疑似されています。

一般に、光の異なる周波数をレンダリングするために異なる方法を使用することで、例えば、ベイクか動的か、オブジェクト毎かレベル毎か、ピクセル毎かの頂点毎か、など、あなたは限られたハードウェア上でフルボディのイメージを作成することができます。要約すると、一般的に高周波と低周波の両方で強力なバリエーションの色または値を持つようにすることをお勧めします。

周波数の演習: Shadowgun を分解

  • 一番上の行
    • 超低周波スペキュラバーテックスライト(Dynamic)
  • 中の行
    • スペキュラバーテックスライト * アルファ
  • 一番下
    • 最終的な合計

注意: 通常、これらの分解は、繰延レンダラーでのステップを参照しますが、ここではそうではありません。このすべては一回のパスで行われます。これらこの合成に基づいた 2 つの関連シェーダーです。

頂点ごとに追加したバーチャルグロスでライトマップ

Shader "MADFINGER/Environment/Virtual Gloss Per-Vertex Additive (Supports Lightmap)" {
Properties {
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    //_MainTexMipBias ("Base Sharpness", Range (-10, 10)) = 0.0
    _SpecOffset ("Specular Offset from Camera", Vector) = (1, 10, 2, 0)
    _SpecRange ("Specular Range", Float) = 20
    _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
    _Shininess ("Shininess", Range (0.01, 1)) = 0.078125
    _ScrollingSpeed("Scrolling speed", Vector) = (0,0,0,0)
}

SubShader {
    Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
    LOD 100



    CGINCLUDE
    #include "UnityCG.cginc"
    sampler2D _MainTex;
    float4 _MainTex_ST;
    samplerCUBE _ReflTex;

    #ifdef LIGHTMAP_ON
    float4 unity_LightmapST;
    sampler2D unity_Lightmap;
    #endif

    //float _MainTexMipBias;
    float3 _SpecOffset;
    float _SpecRange;
    float3 _SpecColor;
    float _Shininess;
    float4 _ScrollingSpeed;

    struct v2f {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
        #ifdef LIGHTMAP_ON
        float2 lmap : TEXCOORD1;
        #endif
        fixed3 spec : TEXCOORD2;
    };


    v2f vert (appdata_full v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);

        o.uv = v.texcoord + frac(_ScrollingSpeed * _Time.y);

        float3 viewNormal = UnityObjectToViewPos(v.normal);
        float3 viewPos = UnityObjectToViewPos(v.vertex);
        float3 viewDir = float3(0,0,1);
        float3 viewLightPos = _SpecOffset * float3(1,1,-1);

        float3 dirToLight = viewPos - viewLightPos;

        float3 h = (viewDir + normalize(-dirToLight)) * 0.5;
        float atten = 1.0 - saturate(length(dirToLight) / _SpecRange);

        o.spec = _SpecColor * pow(saturate(dot(viewNormal, normalize(h))), _Shininess * 128) * 2 * atten;

        #ifdef LIGHTMAP_ON
        o.lmap = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
        #endif
        return o;
    }
    ENDCG


    Pass {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 c = tex2D (_MainTex, i.uv);

            fixed3 spec = i.spec.rgb * c.a;

            #if 1
            c.rgb += spec;
            #else           
            c.rgb = c.rgb + spec - c.rgb * spec;
            #endif

            #ifdef LIGHTMAP_ON
            fixed3 lm = DecodeLightmap (tex2D(unity_Lightmap, i.lmap));
            c.rgb *= lm;
            #endif

            return c;
        }
        ENDCG 
    }   
}
}

頂点ごとに追加したバーチャルグロスでライトプローブ

Shader "MADFINGER/Environment/Lightprobes with VirtualGloss Per-Vertex Additive" {
Properties {
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    _SpecOffset ("Specular Offset from Camera", Vector) = (1, 10, 2, 0)
    _SpecRange ("Specular Range", Float) = 20
    _SpecColor ("Specular Color", Color) = (1, 1, 1, 1)
    _Shininess ("Shininess", Range (0.01, 1)) = 0.078125    
    _SHLightingScale("LightProbe influence scale",float) = 1
}

SubShader {
    Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
    LOD 100



    CGINCLUDE
    #pragma multi_compile _ LIGHTMAP_ON
    #include "UnityCG.cginc"
    sampler2D _MainTex;
    float4 _MainTex_ST;


    float3 _SpecOffset;
    float _SpecRange;
    float3 _SpecColor;
    float _Shininess;
    float _SHLightingScale;

    struct v2f {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
        float3 refl : TEXCOORD1;
        fixed3 spec : TEXCOORD3;
        fixed3 SHLighting: TEXCOORD4;
    };


    v2f vert (appdata_full v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.texcoord;

        float3 worldNormal = UnityObjectToWorldDir(v.normal);       
        float3 viewNormal = UnityObjectToViewPos(v.normal);
        float4 viewPos = UnityObjectToViewPos(v.vertex);
        float3 viewDir = float3(0,0,1);
        float3 viewLightPos = _SpecOffset * float3(1,1,-1);

        float3 dirToLight = viewPos.xyz - viewLightPos;

        float3 h = (viewDir + normalize(-dirToLight)) * 0.5;
        float atten = 1.0 - saturate(length(dirToLight) / _SpecRange);

        o.spec = _SpecColor * pow(saturate(dot(viewNormal, normalize(h))), _Shininess * 128) * 2 * atten;

        o.SHLighting    = ShadeSH9(float4(worldNormal,1)) * _SHLightingScale;

        return o;
    }
    ENDCG


    Pass {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 c    = tex2D (_MainTex, i.uv);

            c.rgb *= i.SHLighting;
            c.rgb += i.spec.rgb * c.a;

            return c;
        }
        ENDCG 
    }   
}
}

ベスト・プラクティス

GPU の最適化: アルファテスト

ある種の GPU 、特にモバイルデバイスでは、アルファテスト(つまりピクセルシェーダーでの discardclip の操作)がパフォーマンスのオーバーヘッドになります。可能であれば、アルファテストシェーダーをアルファブレンドに交換するべきです。どうしてもアルファテストを避けることができない場合は、画面にうつるアルファテストのピクセル数を最小限に留めてください。

iOS のテクスチャ圧縮

いくつかの画像、特に iOS/Android の PVR テクスチャ圧縮を使用する場合、アルファチャンネルに視覚的な不都合が出る傾向があります。このような場合には、イメージングソフトウェアで直接 PVRT の圧縮パラメーターを調整する必要があるかもしれません。PVR export plugin をインストールしたり、イマジネーションテックから、PVRTC 形式のクリエイター PVRTexToolを使用して、それらの調整を行うことができます。圧縮の結果できる .pvr 拡張子を持つ画像ファイルを、Unity のエディターへ直接インポートしても、指定した圧縮パラメーターは保持されます。PVRT-圧縮テクスチャでは十分によい画質にならないか、特に鮮明な画像が必要な場合( GUI 用のテクスチャなど)、32 ビットのテクスチャの代わりに、16 ビットの使用も検討できます。そうすることで、メモリ帯域幅および必要なストレージは半分に低減します。

Android のテクスチャ圧縮

OpenGL ES 2.0 をサポートしているすべての Android デバイスは ETC1 圧縮フォーマットをサポートしています。したがって、テクスチャフォーマットはできるだけ ETC1 の使用を奨励します。

NVIDIA Tegra や Qualcomm の Snapdragon など、特定のグラフィックス·アーキテクチャをターゲットにする場合、それらのアーキテクチャで利用可能な独自の圧縮形式の使用を検討する価値はあるかもしれません。Android マーケットはまた、サポートされるテクスチャ圧縮フォーマットに基づいてフィルタリングでき、サポートしていないデバイス上でダウンロードを防止することができる例えば 3D テクスチャと一緒に配布アーカイブ( .apk )を意味します。

エクササイズ

ダウンロード テクセルへのレンダリング. あなたのモデルにライティングをベイクする。 Photoshop で結果にハイパスフィルターを実行します。 “モバイル/キューブマップ”シェーダーを編集し、テクセルパッケージへのレンダリングに含まれ、欠落した低周波の光の詳細は、頂点光によって置換されます。

スクリプトとゲームプレイ方法
スクリプトの最適化