ラベル シェーダ の投稿を表示しています。 すべての投稿を表示
ラベル シェーダ の投稿を表示しています。 すべての投稿を表示

2012年8月25日土曜日

Unityシェーダについて考える ~その2



前回投稿の「Unityシェーダについて考える」の続編です。

さて前回に書いた、たった数行のコードからUnityはどれぐらいの量のコードを自動生成するでしょう?Arasさんのブログから再び翻訳します:
(シェーダ周りは専門家でないので多少の誤訳はご勘弁ください)



http://aras-p.info/blog/page/4/
2010-07-16

自動生成されるコード
Unityのsurface shaderのコードマクロはその後、実際の頂点シェーダ、ピクセルシェーダを生成して、それぞれのプラットフォーム向けにコンパイルする。Unity3.0のデフォルト設定で対応している内容としては:
  • フォワードレンダラーおよびデファードライティングレンダラー(Light Pre-Pass)
  • 事前計算ライトマップのオブジェクト
  • 各種ライト(Directional, Point, Spot Light)対応。シャドウマップ対応、ただしフォワードレンダラーのみであり、これはLight Pre-passでは別処理であることが理由。
  • フォワードレンダラーではVertex LitおよびSphere Harmonicsライトがオブジェクトごとにコンパイルされる。さらに追加のブレンドパスを追加のピクセルごとのライトが異なるパスでレンダリングされる必要がある場合に対応
  • Light Pre-Passレンダラーについて、ベースのパスを生成してNormalや鏡面反射を出力して、最終パスとしてAlbedoをライティングと組み合わせ、ライトマップや自己発光ライティングを追加する
  • オプションとして影のレンダリングパスを生成する(頂点編集のカスタマイズ(Vertex Modifier)が必要なケース、たとえば頂点シェーダのアニメーションとして活用されたり、あるいは複雑な透明度効果が含まれる場合など)
実際の例として、フォワードレンダリングのベースパスとしてひとつのDirectional Light、4つの頂点Point Light、3rd order SH Light、追加のライトマップで生成を設定したケースで、どれだけのコードが自動生成されるのかご覧いただこう:

#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_fwdbase
#include "HLSLSupport.cginc"
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct Input {
    float2 uv_MainTex : TEXCOORD0;
};
sampler2D _MainTex;
sampler2D _BumpMap;
void surf (Input IN, inout SurfaceOutput o)
{
    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_MainTex));
}
struct v2f_surf {
  V2F_POS_FOG;
  float2 hip_pack0 : TEXCOORD0;
  #ifndef LIGHTMAP_OFF
  float2 hip_lmap : TEXCOORD1;
  #else
  float3 lightDir : TEXCOORD1;
  float3 vlight : TEXCOORD2;
  #endif
  LIGHTING_COORDS(3,4)
};
#ifndef LIGHTMAP_OFF
float4 unity_LightmapST;
#endif
float4 _MainTex_ST;
v2f_surf vert_surf (appdata_full v) {
  v2f_surf o;
  PositionFog( v.vertex, o.pos, o.fog );
  o.hip_pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
  #ifndef LIGHTMAP_OFF
  o.hip_lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
  #endif
  float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
  TANGENT_SPACE_ROTATION;
  #ifdef LIGHTMAP_OFF
  o.lightDir = mul (rotation, ObjSpaceLightDir(v.vertex));
  #endif
  #ifdef LIGHTMAP_OFF
  float3 shlight = ShadeSH9 (float4(worldN,1.0));
  o.vlight = shlight;
  #ifdef VERTEXLIGHT_ON
  float3 worldPos = mul(_Object2World, v.vertex).xyz;
  o.vlight += Shade4PointLights (
    unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
    unity_LightColor0, unity_LightColor1, unity_LightColor2, unity_LightColor3,
    unity_4LightAtten0, worldPos, worldN );
  #endif // VERTEXLIGHT_ON
  #endif // LIGHTMAP_OFF
  TRANSFER_VERTEX_TO_FRAGMENT(o);
  return o;
}
#ifndef LIGHTMAP_OFF
sampler2D unity_Lightmap;
#endif
half4 frag_surf (v2f_surf IN) : COLOR {
  Input surfIN;
  surfIN.uv_MainTex = IN.hip_pack0.xy;
  SurfaceOutput o;
  o.Albedo = 0.0;
  o.Emission = 0.0;
  o.Specular = 0.0;
  o.Alpha = 0.0;
  o.Gloss = 0.0;
  surf (surfIN, o);
  half atten = LIGHT_ATTENUATION(IN);
  half4 c;
  #ifdef LIGHTMAP_OFF
  c = LightingLambert (o, IN.lightDir, atten);
  c.rgb += o.Albedo * IN.vlight;
  #else // LIGHTMAP_OFF
  half3 lmFull = DecodeLightmap (tex2D(unity_Lightmap, IN.hip_lmap.xy));
  #ifdef SHADOWS_SCREEN
  c.rgb = o.Albedo * min(lmFull, atten*2);
  #else
  c.rgb = o.Albedo * lmFull;
  #endif
  c.a = o.Alpha;
  #endif // LIGHTMAP_OFF
  return c;
}

実に90行のコード!

このうち、10行は自身のsurface shaderのコードであり、残りの80行はUnity 2.xの際は手書きが必要であったものである。(まあ、そもそも2.xはここまでライティング対応できてなかったから行数もここまでなかっただろうが)。さらに言ってしまうと、これはフォワードレンダラーのベースパスだけなのである。さらに追加のパスのコードも生成するし、デファードのベースパスも生成するし、デファードの最終パスも生成するし、影のパスも生成するし、まだまだ処理はあるのだ。

要するに、シェーダはより簡潔に書かれるべきなのである(少なくとも私にとって)。これによりshaderを書くUnityユーザが3倍ぐらいに増えてほしいものである。(今は10人だから30人か!)ライティングに関する変更予定のパイプラインが次のUnityバージョンで予定されているが、互換性がより高まるだろう。
----------

おー。コードは8倍に膨れ上がるのですね。これを自動生成とはさすがUnity!

しかしAras氏のブログは色々と気持ちがこもっているので、難しい用語は少し読み飛ばしながら読んでもなかなか読み応えがある内容ですね。学習に生かしたいものです。

世界でも数少ないシェーダをマスターしているプログラマを目指そう!

2012年8月24日金曜日

Unityシェーダについて考える ~その1


最近Unityアジアブートキャンプの内容が公開された。

[Unity3D]Unityアジアブートキャンプの内容の一部を一般公開 - テラシュールウェア
http://terasur.blog.fc2.com/blog-entry-239.html

テーマのひとつで「ShaderLab入門」紹介があったのだが、UnityではAras Pranckevičiusさんという方がでこのエリアをご担当されており、そのブログの内容からUnityがシェーダに対して目指していることが分かるのでちょっと紹介をしたい。



http://aras-p.info/blog/page/4/
2010-07-16

「シェーダ死すべし」という自分の発見から1年が経った。

どうしたことかUnity3になって、いよいよこの発見が実現に至ることになった。新機能をSurface Shaderという名前にした理由は「シェーダ死すべし」としたかったものの、それではイカンだろうと判断してグッとこらえたからである(笑)

実現したいこと
主に実現したいこととしてはサーフェイスのプロパティ宣言に時間の90%をかけてあとは余計なことをしたくないということだ。言い換えると:

「おーい、Albedoにこのテキスチャを適用して、ノーマルはこのノーマルマップを使うぞ。あとBlinn-Phongのライティングモデルを使うからな。それ以上は面倒だからいちいち聞いてくれるなよ!」

もっと言うと、フォワードレンダリングやデファードレンダリングであるか、ライトタイプの処理や、どれだけの数のライトをフォワードレンダラーで処理して、間接照明のSHプローブの処理をどうするかとか、まったく興味がないよ!ってことだ。汚れ仕事はレンダリングプログラマーの仕事なんだから、なんとかしろや!と。

具体例
簡単な事例だけど、完全に使えるUnity3.0シェーダで、拡散ライティングでテキスチャ、ノーマルマップつかったものを紹介する:

良いモデルとテキスチャの組み合わせで結構生かす画像ができているだろ!どんなもんだい。(注:画像は冒頭の画像)
Shader "Example/Diffuse Bump" {
  Properties {
    _MainTex ("Texture", 2D) = "white" {}
    _BumpMap ("Bumpmap", 2D) = "bump" {}
  }
  SubShader {
    Tags { "RenderType" = "Opaque" }
    CGPROGRAM
    #pragma surface surf Lambert
    struct Input {
      float2 uv_MainTex;
      float2 uv_BumpMap;
    };
    sampler2D _MainTex;
    sampler2D _BumpMap;
    void surf (Input IN, inout SurfaceOutput o) {
      o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    }
    ENDCG
  } 
  Fallback "Diffuse"
}

コードのなかで面白味に欠けるところはグレーにしてみた(※本ブログではグレーにできなかったが、CGPROGRAM~ENDCG以外はグレー)。(宣言として、シリアライズしたシェーダのプロパティやUI名、古い機械向けに使用するシェーダ(Fallback)、等)そうすると残るのはCg/HLSLコードで、これは自動生成で大量のコード(ライティングや、その他諸々)に変換してくれる。

上記のシェーダはいくつかのパーツに分解できる:

  • surfをメイン関数としたsurface shaderで、Lambertライティングモデルを使用する。Lambertはデフォルト選択できるモデルのひとつだが、自分で書くことも出来る。
  • surface shaderからデータの入力を行う。これもデフォルト選択できる入力から選ぶことも出来て、頂点ごとに計算されピクセルごとにsurface関数に渡される。この例ではふたつのテキスチャ位置を設定する。
  • 実際のsurface shaderコードにあたる。入力値をとり、SurfaceOutput(デフォルトで存在する構造体)に書き込むことができる、ただしそのライティングモデルにその構造体に対応していることが前提となる。実際のコードはAlbedoおよびNormalを出力に指定するのみである。

-----

うーん。まったく別世界の話なので分かっていないところが多く誤訳などあったらご勘弁を。

次回はいかにUnityがコードを自動生成してくれているか紹介する。やはりコードは自身で書く部分は少ないにこしたことはないと感じる。

世界でも数少ないシェーダを使いこなすプログラマになろう!

ブックマークに追加

このエントリーをはてなブックマークに追加

自己紹介

自分の写真
Unity3D公式マニュアル翻訳やってる人がスマホ(iPhone, Android)のゲーム開発しています。気軽に面白く初心者が遊べる内容がモットー。Blogでは開発情報をひたすら、Twitterではゲーム作成の過程で参考にしている情報を中心につぶやきます

ページビューの合計

過去7日間の人気投稿