Part1 https://qiita.com/YukiMiyatake/items/f71aa766de336fbc6781
#Ambient
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_5
物理ベースだと 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に乗算すればいい
#複数ライト
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_6
UNITYでは複数のライトを扱うことが出来る
Forwardレンダリングモードでは
一番影響の高いディレクショナルライトのみが ForwardBaseで処理され
その他のライトは ForwardAdd で処理される
今の状態では ForwardBaseしか書いていないので、2つ目のライトを与えても
反映されないので
ForwardAddパスを追加する
シェーダーの中身は ForwardBaseと同じでよい
ただし Ambientライトは1回しか加えてはいけないので ForwardAddでは加算しない
ブレンドモードも OneOne などにしなければうまく加算されないので ブレンドモードも気をつけること
これで複数ライトの影響を与えられるようになった
#Vertexシェーダでのライト計算
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_7
今までは 補間された法線をつかい、フラグメントシェーダでピクセル単位でライト計算をしていた
頂点数にくらべ ピクセル数のほうが圧倒的に多く、可能であれば計算は頂点シェーダにまかせたい
ので、試しに
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;
}
今まで行っていたライト計算を 頂点シェーダにもってきただけだ
それにより フラグメントシェーダが 非常にコンパクトになり
そうとう速度が速くなったと思われる
肝心のレンダリング結果
予想以上に変化がなかった
もちろん モデルのポリゴン数やライトの状況によっては 全く使えない事もあるし
そもそも 法線マッピングは メインライト以外影響しなくなるので
限定的ではあるが 覚えておいて損はないかと。
私はパフォーマンスチューニングで結構使います