このセクションでは、最適化のレンダリングを技術的に紹介します。パフォーマンス向上のためのライティング結果を焼く方法と Shadowgun の開発者が彼らのゲームを素晴らしいものに見えるようにするライティングをベイクして高コントラストのテクスチャをレバレッジする方法を示します。モバイルに最適化されたゲームがどのように見えるか、一般的な情報を探している場合は、グラフィックスメソッドページをチェックしてください。
時には、あなたのゲーム内のレンダリングを最適化することは、いくつかの割に合わない仕事を必要とします。Unity が提供するストラクチャーのすべては何かを速く動作させるのが容易になりますが、限られたハードウェア上で忠実に動作する必要がある場合、あなたが、自分自身でもっとより速くする鍵となるストラクチャーの変化を導入することができ、ストラクチャーを回避して行く方法でより高速になものになります。選択するあなたのツールは、エディターのスクリプト、シンプルなシェーダーと昔ながらのアート制作です。
まず最初に、この マテリアルとシェーダーとテクスチャをチェックしてください。
#pragma debug
)
#pragma DEBUG
を追加した場合は、インスペクター経由でコンパイルされたシェーダーを開くと、中間 CG コードを見ることができます。これは、シェーダーの特定の部分が実際にはどのように計算されるか、検査するのに便利で、また、あなたがサーフェスシェーダーから欲しい特定の側面をつかむためや CG シェーダーに適用するのに使えます。Shadowgun は、それを実行するハードウェアを考慮した素晴らしいグラフィカルな成果です。アートの質はパズルの鍵であると言われますが、そのような品質を達成するために、プログラマは彼らのアーティストの可能性を最大限引き出させるカップルのトリックがあります。
グラフィックスメソッドページでは、すばらしい最適化の例として Shadowgun の黄金の彫像を使用してます:それらの彫像に通常のマップを使用していくつかの固体を定義を与える代わりに彼らはテクスチャにライティングのディテールをベイクしました。ここに、あなたがあなた自身のゲームで同様のテクニックをどのように、なぜ、使用するのか表示しています。
// これは、静的にライトマップしたジオメトリ上に法線マップした
// スペキュラーハイライトを描画するピクセルシェーダーコードです。
// 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 つの方法は、Photoshop で“ハイパス”フィルターを使用することです。Filters->Other->High Pass . あなたが以前にオーディオの作業を行っている場合は、あなたはハイパスの意味がわかるでしょう。本質的に、すべての周波数のうち、X よりも低いと遮断され、通過させるパラメーターをフィルターに渡します。画像の場合、ガウスぼかし、低域を通過するのと同じです。
周波数は物事を分離することと、何か処理方法を決定するよい方法であるため、リアルタイムグラフィックスに適用されます。例えば、基本的なライトマップ環境では、最終画像は低い周波数であるライトマップと高周波であるテクスチャの合成によって得られます。Shadowgun では、低周波数の光は、迅速に光プローブを用いて文字に適用され、高周波光は、任意の光の方向を持つ単純なバンプマップシェーダーを使用することによって疑似されています。
一般に、光の異なる周波数をレンダリングするために異なる方法を使用することで、例えば、ベイクか動的か、オブジェクト毎かレベル毎か、ピクセル毎かの頂点毎か、など、あなたは限られたハードウェア上でフルボディのイメージを作成することができます。要約すると、一般的に高周波と低周波の両方で強力なバリエーションの色または値を持つようにすることをお勧めします。
注意: 通常、これらの分解は、繰延レンダラーでのステップを参照しますが、ここではそうではありません。このすべては一回のパスで行われます。これらこの合成に基づいた 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 、特にモバイルデバイスでは、アルファテスト(つまりピクセルシェーダーでの discard と clip の操作)がパフォーマンスのオーバーヘッドになります。可能であれば、アルファテストシェーダーをアルファブレンドに交換するべきです。どうしてもアルファテストを避けることができない場合は、画面にうつるアルファテストのピクセル数を最小限に留めてください。
いくつかの画像、特に iOS/Android の PVR テクスチャ圧縮を使用する場合、アルファチャンネルに視覚的な不都合が出る傾向があります。このような場合には、イメージングソフトウェアで直接 PVRT の圧縮パラメーターを調整する必要があるかもしれません。PVR export plugin をインストールしたり、イマジネーションテックから、PVRTC 形式のクリエイター PVRTexToolを使用して、それらの調整を行うことができます。圧縮の結果できる .pvr 拡張子を持つ画像ファイルを、Unity のエディターへ直接インポートしても、指定した圧縮パラメーターは保持されます。PVRT-圧縮テクスチャでは十分によい画質にならないか、特に鮮明な画像が必要な場合( GUI 用のテクスチャなど)、32 ビットのテクスチャの代わりに、16 ビットの使用も検討できます。そうすることで、メモリ帯域幅および必要なストレージは半分に低減します。
OpenGL ES 2.0 をサポートしているすべての Android デバイスは ETC1 圧縮フォーマットをサポートしています。したがって、テクスチャフォーマットはできるだけ ETC1 の使用を奨励します。
NVIDIA Tegra や Qualcomm の Snapdragon など、特定のグラフィックス·アーキテクチャをターゲットにする場合、それらのアーキテクチャで利用可能な独自の圧縮形式の使用を検討する価値はあるかもしれません。Android マーケットはまた、サポートされるテクスチャ圧縮フォーマットに基づいてフィルタリングでき、サポートしていないデバイス上でダウンロードを防止することができる例えば 3D テクスチャと一緒に配布アーカイブ( .apk )を意味します。
ダウンロード テクセルへのレンダリング. あなたのモデルにライティングをベイクする。 Photoshop で結果にハイパスフィルターを実行します。 “モバイル/キューブマップ”シェーダーを編集し、テクセルパッケージへのレンダリングに含まれ、欠落した低周波の光の詳細は、頂点光によって置換されます。