Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

21,243 views

Published on

  • Be the first to comment

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 のためにマテリアルを共通 化• テクスチャは結構な量を使っても大丈夫 – ただし圧縮フォーマット前提• 静的なライティングで十分• 処理を頂点シェーダに可能な限り持って いく – = ピクセル単位での処理を減らす• シェーダの計算は精度を落とす

×