Part1 https://qiita.com/YukiMiyatake/items/f71aa766de336fbc6781
Ambient
物理ベースだと GI(グローバルイルミネーション)を使う
これは光の回析などを計算したライトモデル
ただし古典シェーダでは一律に明るさをあたえる Ambientライト
設定
設定はメニューの
Window -> Lighting -> Setting
Environment Lightingで設定します
Sourceを Colorにすると Ambient色も変更可能
シェーダ
シェーダからは UNITY_LIGHTMODEL_AMBIENT として取得できる
基本的にAmbientは 他のカラーに足せば良い
前回の続きで、ディフューズ、スペキュラーに足してみる
float3 frag(v2f i) : SV_Target
{
float3 L = normalize(_WorldSpaceLightPos0.xyz);
float3 V = normalize(_WorldSpaceCameraPos - i.vertexW.xyz);
float3 N = i.normal;
float3 H = normalize(L + V);
//Ambient
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
// texture albedo
float4 tex = tex2D(_MainTex, i.uv);
// Diffuse(HalfLambert)
float3 NdotL = dot(N, L);
float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb;
// Speculer
// float3 specular = pow(max(0.0, dot(reflect(-L, N), V)), _Spec1Power) * _Spec1Color.xyz; // reflection
float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz; // Half vector
return (ambient + diffuse) * tex + specular;
}
これで、Ambientが反映される
ライトの強さ
今まではライトの角度のみで強さに関しては無視していたので
ライトの強度を反映させる
LIGHT_ATTENUATION を使うために AutoLight.cgincを includeする
float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);
float3 diffuse = (NdotL*0.5 + 0.5) * lightCol;
float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz * lightCol; // Half vector
こんな感じで、光源色と強さをdiffuseやSpecularに乗算すればいい
複数ライト
UNITYでは複数のライトを扱うことが出来る
Forwardレンダリングモードでは
一番影響の高いディレクショナルライトのみが ForwardBaseで処理され
その他のライトは ForwardAdd で処理される
今の状態では ForwardBaseしか書いていないので、2つ目のライトを与えても
反映されないので
ForwardAddパスを追加する
シェーダーの中身は ForwardBaseと同じでよい
ただし Ambientライトは1回しか加えてはいけないので ForwardAddでは加算しない
ブレンドモードも OneOne などにしなければうまく加算されないので ブレンドモードも気をつけること
これで複数ライトの影響を与えられるようになった
Vertexシェーダでのライト計算
今までは 補間された法線をつかい、フラグメントシェーダでピクセル単位でライト計算をしていた
頂点数にくらべ ピクセル数のほうが圧倒的に多く、可能であれば計算は頂点シェーダにまかせたい
ので、試しに
ForwardBaseは今までどおりの フラグメントシェーダでのライティングを行い
ForwardAddでは 頂点シェーダで頂点ごとライティング計算を行い補間を行い フラグメントシェーダではライト計算を行わない事にした
ただし、スペキュラのような ピーキーなライトでは、頂点による補間ではよい結果が出ないことが想定されるので
あくまでテスト
ForwardAddの構造体
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD1;
float3 diffuse : TEXCOORD2;
float3 specular : TEXCOORD3;
};
フラグメントシェーダではもうライティング計算をおこなわないので、ワールド座標も法線も不要
頂点シェーダで diffuseとspecularを計算して 補完し送るだけだ
ここでセマンティクスを TEXCOORD2、TEXCOORD3にしているのは あまり理由がない
たしか今のHLSLでは TEXCOORDとCOLORには明確な違いがなくなったと思ってる
ので 多分どちらを使っても良いと思う
ForwardAddシェーダーの中身
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
float4 vertexW = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float3 normal = UnityObjectToWorldNormal(v.normal);
float3 L = normalize(_WorldSpaceLightPos0.xyz);
float3 V = normalize(_WorldSpaceCameraPos - vertexW.xyz);
float3 N = normal;
float3 H = normalize(L + V);
float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);
float3 NdotL = dot(N, L);
o.diffuse = (NdotL*0.5 + 0.5) * lightCol;
o.specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz * lightCol; // Half vector
return o;
}
float3 frag(v2f i) : SV_Target
{
// texture albedo
float4 tex = tex2D(_MainTex, i.uv);
return i.diffuse * tex + i.specular;
}
今まで行っていたライト計算を 頂点シェーダにもってきただけだ
それにより フラグメントシェーダが 非常にコンパクトになり
そうとう速度が速くなったと思われる
肝心のレンダリング結果
予想以上に変化がなかった
もちろん モデルのポリゴン数やライトの状況によっては 全く使えない事もあるし
そもそも 法線マッピングは メインライト以外影響しなくなるので
限定的ではあるが 覚えておいて損はないかと。
私はパフォーマンスチューニングで結構使います