Scripting and Gameplay Methods
Optimizing Scripts

レンダリングの最適化

Suggest a change

Success!

Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable.

Close

Sumbission failed

For some reason your suggested change could not be submitted. Please try again in a few minutes. And thank you for taking the time to help us improve the quality of Unity Documentation.

Close

Cancel

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

Get Artsy!

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

Unityインディーユーザーのための注意: ここで参照するエディタースクリプトは,スムーズな生産を行うためにRenderTexturesを使用し,それらはすぐに動作します。しかし,その背後にある原則は同様にスクリーンショットと連携し,あなたがそれらの技術を使用していくつかのテクスチャのために独自にベイクするのを止めません。

フードに下に潜る方法

まず最初に,このシェーダが書かれている方法の紹介をチェックしてください。

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

徹底的にShadowgun

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

In the Graphics Methods page we used the golden statue in Shadowgun as an example of a great optimization; instead of using a normal map to give their statue some solid definition, they just baked lighting detail into the texture. Here, we will show you how and why you should use a similar technique in your own game.

Shader code for Real-Time vs Baked Golden Statue

// This is the pixel shader code for drawing normal-mapped
// specular highlights on static lightmapped geometry

// 5 texture reads, lots of instructions

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);
// This is the pixel shader code for lighting which is
// baked into the texture

// 2 texture reads, very few instructions

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

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

return c;

Reflective Bumped Specular Baked Light with Reflection

Render to Texel

The real-time light is definitely higher quality, but the performance gain from the baked version is massive. So how was this done? An editor tool called Render to Texel was created for this purpose. Note that you will need to be running Unity PRO in order to use this tool. It bakes the light into the texture through the following process:

  • Transform the tangent space normal map to world space via script.
  • Create a world space position map via script.
  • Render to Texture a fullscreen pass of a the entire texture using the two previous maps, with one additional pass per light.
  • Average results from several different vantage points to yield something which looks plausible all around, or at least from common viewing angles in your game.

This is how the best graphics optimizations work. They sidestep tons of calculations by preforming them in the editor or before the game runs. In general, this is what you want to do:

  • Create something that looks great, don’t worry about performance.
  • Use tools like Unity’s lightmapper and editor extensions like Render to Texel and Sprite Packer to bake it down to something which is very simple to render.
    • Making your own tools is the best way to do this, you can create the perfect tool suited for whatever problem your game presents.
  • Create shaders and scripts which modulate your baked output to give it some sort of “shine”; an eye-catching effect to create an illusion of dynamic light.

Concept of Light Frequency

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

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

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

周波数の演習:Shadowgunを分解

  • Top Row
    • Ultra-Low-Frequency Specular Vertex Light (Dynamic) | High Frequency Alpha Channel | Low Frequency Lightmap | High Frequency Albedo
  • Mid Row
    • Specular Vertex Light * Alpha | High Frequency Additive Details | Lightmap * Color Channel
  • Bottom
    • Final Sum

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

Lightmapped with Virtual Gloss Per-Vertex Additive

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;

    #ifndef LIGHTMAP_OFF
    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;
        #ifndef LIGHTMAP_OFF
        float2 lmap : TEXCOORD1;
        #endif
        fixed3 spec : TEXCOORD2;
    };


    v2f vert (appdata_full v)
    {
        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

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

        float3 viewNormal = mul((float3x3)UNITY_MATRIX_MV, v.normal);
        float4 viewPos = mul(UNITY_MATRIX_MV, 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;

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


    Pass {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #pragma fragmentoption ARB_precision_hint_fastest       
        fixed4 frag (v2f i) : COLOR
        {
            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

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

            return c;
        }
        ENDCG 
    }   
}
}

Lightprobes with Virtual Gloss Per-Vertex Additive

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_OFF 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 = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.texcoord;

        float3 worldNormal = mul((float3x3)_Object2World, v.normal);        
        float3 viewNormal = mul((float3x3)UNITY_MATRIX_MV, v.normal);
        float4 viewPos = mul(UNITY_MATRIX_MV, 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
        #pragma fragmentoption ARB_precision_hint_fastest       
        fixed4 frag (v2f i) : COLOR
        {
            fixed4 c    = tex2D (_MainTex, i.uv);

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

            return c;
        }
        ENDCG 
    }   
}
}

Best Practices

GPU optimization: Alpha-Testing

いくつかのGPU,特にモバイル機器で,アルファテストのため高いパフォーマンスのオーバーヘッドの発生を見つけます。( __discard__and__clip__ピクセルシェーダでの操作のまたは使用で)。あなたは可能であればアルファブレンドと一緒にアルファテストシェーダを交換する必要があります。アルファテストを避けることができない場合,最小限のアルファテストされた画素の総数を維持する必要があります。

iOSのテクスチャ圧縮

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

Androidのテクスチャ圧縮

OpenGL ES 2.0をサポートしているすべてのAndroidデバイスはETC1 圧縮フォーマットをサポート;したがって,可能な限り,好ましいテクスチャフォーマットとしてETC1の使用を奨励します。

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

エクササイズ(Unity Pro専用)

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

Scripting and Gameplay Methods
Optimizing Scripts