Shadow gunのサンプルから学べるモバイル最適化

19,037 views

Published on

0 Comments
31 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
19,037
On SlideShare
0
From Embeds
0
Number of Embeds
9,684
Actions
Shares
0
Downloads
95
Comments
0
Likes
31
Embeds 0
No embeds

No notes for slide

Shadow gunのサンプルから学べるモバイル最適化

  1. 1. ShadowGun Sample Level から学べるモバイル最適化 株式会社 Aiming リードソフトウェアエンジニア 牧野克俊 2012/06/01
  2. 2. 自己紹介• オンラインゲームを長年作ってきま した• 主にサーバや通信部分担当です!• クライアントや 3D は自信ないです
  3. 3. でもがんばって解説します!
  4. 4. • 基本的に多くの情報はすでに英語で Unity の Web サイトに出ています – http://blogs.unity3d.com/2012/03/23/shad owgun-optimizing-for-mobile-sample-level/ – http://blogs.unity3d.com/2011/08/18/fast- mobile-shaders-talk-at-siggraph/
  5. 5. ShadowGun
  6. 6. 概要• データを見てみよう• シェーダから学ぶ• テクニックを解説してみる
  7. 7. データを見てみよう
  8. 8. Scene 内データ数• GameObject : 795• メッシュ(MeshFilter) : 429 –不透明 : 369 –半透明 : 60
  9. 9. Scene 内データ数• マテリアル : 40• シェーダ : 17• ライト : 92 –Directional: 1 –Point :91
  10. 10. テクスチャ枚数• 64 × 1枚• 128 × 1枚• 256 × 7枚• 512 × 10枚• 1024 × 10枚• 2048 × 3枚
  11. 11. テクスチャ用途• 64、128 – 光の表現、God Ray、BRDF LookUpTexture• 256 – 遠景(月、山)、煙、床、水面• 512 – 彫像、床、旗、空、NormalMap• 1024 – 敵、シャトル、壁、窓• 2048 – 壁、オブジェクト用アトラス
  12. 12. テクスチャフォーマット• 64、128 – RGBA 32、RGB 16• 256 – RGBA 32、RGB 16、Compressed• 512、1024、2048 – Compressed
  13. 13. ポリゴン数• ポリゴン数 – 毎フレーム : 20k 〜 30k 描画 – 敵1体 : 約2k – Draw-call : 30 〜 50 – Batched : 120 〜 150
  14. 14. レンダリングオブジェクト数• 不透明 : 349• 半透明 : 49
  15. 15. レンダリングオブジェクト数• 不透明 : 349 – 5 種類のマテリアルで約 300 – この 5 種類は同じシェーダ• 半透明 : 49 – 3 種類のマテリアルで約 30
  16. 16. 処理時間• physx: 0.3• animation: 0.3• culling 0.0• skinning: 2.6• batching: 0.7• render: 1.8• fixed-update-count: 1 .. 4• update: 0.2• fixedUpdate: 0.4• coroutines: 0.0
  17. 17. 処理時間• physx: 0.3• animation: 0.3• culling 0.0• skinning: 2.6 結構重い!• batching: 0.7• render: 1.8• fixed-update-count: 1 .. 4• update: 0.2• fixedUpdate: 0.4• coroutines: 0.0
  18. 18. シェーダから学ぶ
  19. 19. Case 1v2f 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;}fixed4 frag (v2f i) : COLOR { fixed4 c = tex2D (_MainTex, i.uv); c.rgb *= i.SHLighting;} c.rgb += i.spec.rgb * c.a; return c; フラグメントシェーダ
  20. 20. Case 2v2f vert (appdata_full v) { v2f o; float3 viewPos = mul(UNITY_MATRIX_MV,v.vertex); float dist = length(viewPos); float nfadeout = saturate(dist / _FadeOutDistNear); float ffadeout = 1 - saturate(max(dist - _FadeOutDistFar,0) * 0.2); ffadeout *= ffadeout; nfadeout *= nfadeout; 頂点シェーダ nfadeout *= nfadeout; nfadeout *= ffadeout; float4 vpos = v.vertex; vpos.xyz -= v.normal * saturate(1 - nfadeout) * v.color.a * _ContractionAmount; o.uv = v.texcoord.xy; o.pos = mul(UNITY_MATRIX_MVP, vpos); o.color = nfadeout * v.color * _Multiplier; return o;}fixed4 frag (v2f i) : COLOR { フラグメントシェーダ return tex2D (_MainTex, i.uv.xy) * i.color;}
  21. 21. • フラグメントシェーダが超シンプ ル!
  22. 22. • フラグメントシェーダが超シンプ ル!–なぜ?
  23. 23. • フラグメントシェーダが超シンプ ル!–なぜ? • 計算量を減らすため • ピクセル数 >>> 頂点数
  24. 24. • Virtual Gloss Per-Vertex Additive (Supports Lightmap) –壁とか床 –一番使用量が多い
  25. 25. Pass { CGPROGRAM pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_faste fixed4 frag (v2f i) : COLOR { ・・・
  26. 26. Pass { CGPROGRAM pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_faste 精度を可能な限り落して fixed4 frag (v2f i) : COLOR { 実行時間を短くす る ・・・
  27. 27. fixed4 frag (v2f i) : COLOR { fixed4 c = tex2D (_MainTex, i.uv); fixed3 spec = i.spec.rgb * c.a; c.rgb += spec; fixed3 lm = DecodeLightmap(tex2D( unity_Lightmap, i.lmap)); c.rgb *= lm; return c;}
  28. 28. fixed4 frag (v2f i) : COLOR { fixed4 c = tex2D (_MainTex, i.uv); fixed3 spec = 明示的に精度を落とす i.spec.rgb * c.a; c.rgb += spec; fixed3 lm = DecodeLightmap(tex2D( unity_Lightmap, i.lmap)); c.rgb *= lm; return c;}
  29. 29. 精度• fixed – 通常 11 bit – -2.0 〜 +2.0 – 精度 1 / 256• half – 通常 16 bit – -60000 〜 +60000 – 精度おおよそ 3 桁• float – 32 bit
  30. 30. 精度• fixed – 通常 11 bit 速い – -2.0 〜 +2.0 – 精度 1 / 256• half – 通常 16 bit – -60000 〜 +60000 – 精度おおよそ 3 桁• float 遅い – 32 bit
  31. 31. 精度• fixed – 色、単位ベクトル• half – その他• float – half で精度が足りないとき
  32. 32. 精度• こんな宣言もできます – lowp、mediump、highp
  33. 33. 精度• こんな宣言もできます – lowp、mediump、highpあくまでヒントでハードウェアによっ てどの精度が使われるか違ってくる
  34. 34. テクニックを学ぼう
  35. 35. 描画順1. 不透明2. Skybox –空、地面 –月、遠景の山3. 半透明
  36. 36. ライティング• ライト – Directional : 1 – Point : 91
  37. 37. ライティング• ライト – Directional : 1 – Point : 91 リアルタイムで計算なんか無 理!
  38. 38. ライティング• 静的オブジェクト → Lightmap• 動的オブジェクト → LightProbe
  39. 39. 静的オブジェクトのライティン グ Lightmap + Per Vertex Specular Maps
  40. 40. Per Vertex Specular Maps1. テクスチャの α に鏡面反射強度を格 納しておく2. 頂点シェーダでスペキュラ計算3. フラグメントシェーダで上記計算結 果と強度を掛けて足す
  41. 41. コードv2f vert (appdata_full v) { ・・・ o.spec = _SpecColor * pow(saturate(dot(viewNormal, normalize(h))), _Shininess * 128) * 2 * atten; ・・・}
  42. 42. コードhalf4 frag (v2f i) : COLOR { half4 c = tex2D (_MainTex, i.uv); half3 spec = i.spec.rgb * c.a;}
  43. 43. 動的オブジェクトのライティン グ LightProbe + BRDF
  44. 44. BRDF とはBidirectional Reflectance Distribution Function = 双方向反射率分布関数
  45. 45. BRDF とは物質の質感をあらわすパラメータで、物質に光が入射してきたときに、どのように反射するのか記述したもの
  46. 46. BRDF• 事前に LookupTexture を作成 – RGB にディフューズ – α にスペキュラ• ライトとビュー方向から LookupTexture か ら値を取得• アルベドと上記値を乗算
  47. 47. コードvoid surf (Input IN, inout MySurfaceOutput o) { fixed4 tex =tex2D(_MainTex, IN.uv_MainTex); o.Albedo = tex.rgb; o.Gloss = tex.a; o.Alpha = tex.a; o.Normal = tex2D(_BumpMap, IN.uv_BumpMap).rgb * 2.0 - 1.0;}
  48. 48. コードixed4 LightingPseudoBRDF (MySurfaceOutput s, fixed3lightDir, fixed3 viewDir, fixed atten){ fixed3 halfDir = normalize (lightDir + viewDir); // N.L fixed NdotL = dot (s.Normal, lightDir); // N.H fixed NdotH = dot (s.Normal, halfDir); // remap N.L from [-1..1] to [0..1] fixed biasNdotL = NdotL * 0.5 + 0.5; ・・・
  49. 49. コード ・・・ fixed4 l = tex2D (_BRDFTex, fixed2(biasNdotL, NdotH)); fixed4 c; c.rgb = s.Albedo * (l.rgb + s.Gloss * l.a) * 2; c.a = 0; return c;} s.Gloss(= tex.a)
  50. 50. GodRay
  51. 51. Godray• ポストプロセスで実装されることが 多い• モバイルでポストプロセスはコスト が高い
  52. 52. Godray• ポストプロセスで実装されることが 多い• モバイルでポストプロセスはコスト が高い – 板ポリとテクスチャ – 距離に応じて縮小、透明度を上げる
  53. 53. Godray• ポストプロセスで実装されることが 多い• モバイルでポストプロセスはコスト が高い – 板ポリとテクスチャ – 距離に応じて縮小、透明度を上げる • 画面いっぱいに広がると負荷が高い • それっぽく見せるため
  54. 54. コードv2f vert (appdata_full v) { v2f o; float3 viewPos = mul(UNITY_MATRIX_MV,v.vertex); float dist = length(viewPos); float nfadeout = saturate(dist / _FadeOutDistNear); float ffadeout = 1 - saturate(max(dist - _FadeOutDistFar,0)* 0.2); 距離に応じての減衰率を出す ffadeout *= ffadeout; ・・・
  55. 55. コード ・・・ float4 vpos = v.vertex; vpos.xyz -= v.normal * saturate(1 - nfadeout) * v.color.a* _ContractionAmount; 頂点を法線方向に移動 o.uv = v.texcoord.xy; o.pos = mul(UNITY_MATRIX_MVP, vpos); o.color = nfadeout * v.color * _Multiplier; return o;}
  56. 56.
  57. 57. 旗• 頂点カラーの α に風の影響を受ける かを格納してある• 上記と各種パラメータを使って頂点 シェーダで計算して直接頂点の位置 を変更
  58. 58.
  59. 59. 煙• 通常はパーティクルで作成
  60. 60. 煙• 普通はパーティクルで作成• 少しでも軽くするために – テクスチャの 2 重スクロール – 頂点カラーとブレンド – 端っこの頂点カラーを透明にする
  61. 61. コードv2f vert (appdata_full v) { v2f o; テクスチャ座標移動 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv.xy = TRANSFORM_TEX(v.texcoord.xy,_MainTex) + frac(float2(_ScrollX, _ScrollY) * _Time); o.uv.zw = TRANSFORM_TEX(v.texcoord.xy,_DetailTex) + frac(float2(_Scroll2X, _Scroll2Y) * _Time); ・・・
  62. 62. コード ・・・ o.uv.x += sin(_Time * _SineFreqX) * _SineAmplX; o.uv.y += sin(_Time * _SineFreqY) * _SineAmplY; o.uv.z += sin(_Time * _SineFreqX2) * _SineAmplX2; o.uv.w += sin(_Time * _SineFreqY2) * _SineAmplY2; o.color = _MMultiplier * _Color * v.color; return o;}
  63. 63. Sphere Ambient Occlusion
  64. 64. Sphere Ambient Occlusion• ある点がどれだけ遮蔽されているか に応じて陰をつける• ShadowGun ではキャラクターの影で 使用している – 両足、腰に球を置いてその影を投影
  65. 65. Sphere Ambient Occlusion• なんか小難しい式をあれこれする と最終的に
  66. 66. 参考資料• http://www.iquilezles.org/www/articles/spher eao/sphereao.htm/
  67. 67. void surf (Input IN, inout SurfaceOutput o) {fixed4 tex = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = tex.rgb; o.Alpha = tex.a; half3 t = _Center.xyz - IN.worldPos.xyz; half d = 1 / (t.x * t.x + t.y * t.y + t.z * t.z); o.Gloss = (_Radius * _Radius * d); WorldNormalVector(IN, o.Normal);}
  68. 68. コードfixed4 LightingSimpleLambertAO (SurfaceOutput s, fixed3lightDir, fixed atten) { fixed NdotL = dot (s.Normal, lightDir); fixed bl = NdotL * s.Gloss; fixed4 c; 光のブロック率を出す c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2) * (1 - bl); c.a = s.Alpha; return c;}
  69. 69. まとめ
  70. 70. まとめ• StaticBatching のためにマテリアルを共通 化• テクスチャは結構な量を使っても大丈夫 – ただし圧縮フォーマット前提• 静的なライティングで十分• 処理を頂点シェーダに可能な限り持って いく – = ピクセル単位での処理を減らす• シェーダの計算は精度を落とす

×