Se ha denunciado esta presentación.
Se está descargando tu SlideShare. ×

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

Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio

Eche un vistazo a continuación

1 de 70 Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

Anuncio

Similares a Shadow gunのサンプルから学べるモバイル最適化 (20)

Anuncio

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

×