本日はMRTKShaderの調査枠です。
前回に引き続きMRKTStandardShaderのRimLightを見ていきます。
〇appdata_t構造体
aoodata_t構造体では頂点シェーダーで扱うデータを格納します。
リムライトに直接関するものはありません。
struct appdata_t { float4 vertex : POSITION; // The default UV channel used for texturing. float2 uv : TEXCOORD0; // Used for smooth normal data (or UGUI scaling data). float4 uv2 : TEXCOORD2; // Used for UGUI scaling data. float2 uv3 : TEXCOORD3; fixed3 normal : NORMAL; #if defined(_NORMAL_MAP) fixed4 tangent : TANGENT; #endif UNITY_VERTEX_INPUT_INSTANCE_ID };
〇v2f構造体
v2f構造体は頂点シェーダーで処理されたデータをフラグメントシェーダーに渡すためのデータを格納する構造体です。
appdata_t構造体同様リムライトに直接関連するものはありません。
struct v2f { float4 position : SV_POSITION; #if defined(_UV) float2 uv : TEXCOORD0; #endif #if defined(_WORLD_POSITION) float3 worldPosition : TEXCOORD2; #endif #if defined(_SCALE) float3 scale : TEXCOORD3; #endif #if defined(_NORMAL) #if defined(_NORMAL_MAP) fixed3 tangentX : COLOR3; fixed3 tangentY : COLOR4; fixed3 tangentZ : COLOR5; #else fixed3 worldNormal : COLOR3; #endif #endif UNITY_VERTEX_OUTPUT_STEREO };
〇頂点シェーダー
頂点シェーダーではオブジェクトの頂点にかかわる処理が行われます。
ここでは主に[オブジェクトの持つ頂点座標をUnityの座標系に変更する]、[オブジェクトの持つ法線情報をの処理]、[UV情報の処理]が行われています。
v2f vert(appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); float4 vertexPosition = v.vertex; #if defined(_WORLD_POSITION) float3 worldVertexPosition = mul(unity_ObjectToWorld, vertexPosition).xyz; #endif #if defined(_SCALE) o.scale.x = length(mul(unity_ObjectToWorld, float4(1.0, 0.0, 0.0, 0.0))); o.scale.y = length(mul(unity_ObjectToWorld, float4(0.0, 1.0, 0.0, 0.0))); o.scale.z = length(mul(unity_ObjectToWorld, float4(0.0, 0.0, 1.0, 0.0))); #endif fixed3 localNormal = v.normal; #if defined(_NORMAL) fixed3 worldNormal = UnityObjectToWorldNormal(localNormal); #endif o.position = UnityObjectToClipPos(vertexPosition); #if defined(_WORLD_POSITION) o.worldPosition.xyz = worldVertexPosition; #endif #if defined(_UV) o.uv = TRANSFORM_TEX(v.uv, _MainTex); #endif #if defined(_NORMAL) #if defined(_NORMAL_MAP) fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w; fixed3 worldBitangent = cross(worldNormal, worldTangent) * tangentSign; o.tangentX = fixed3(worldTangent.x, worldBitangent.x, worldNormal.x); o.tangentY = fixed3(worldTangent.y, worldBitangent.y, worldNormal.y); o.tangentZ = fixed3(worldTangent.z, worldBitangent.z, worldNormal.z); #else o.worldNormal = worldNormal; #endif #endif return o; }
〇フラグメントシェーダー
フラグメントシェーダーではピクセルごとの処理を行います。
fixed4 frag(v2f i, fixed facing : VFACE) : SV_Target { fixed4 albedo = tex2D(_MainTex, i.uv); albedo *= _Color; // Normal calculation. #if defined(_NORMAL) fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPosition.xyz)); fixed3 worldNormal; #if defined(_NORMAL_MAP) fixed3 tangentNormal = UnpackScaleNormal(tex2D(_NormalMap, i.uv), _NormalMapScale); worldNormal.x = dot(i.tangentX, tangentNormal); worldNormal.y = dot(i.tangentY, tangentNormal); worldNormal.z = dot(i.tangentZ, tangentNormal); worldNormal = normalize(worldNormal) * facing; #else worldNormal = normalize(i.worldNormal) * facing; #endif #endif fixed pointToLight = 1.0; fixed3 fluentLightColor = fixed3(0.0, 0.0, 0.0); // Blinn phong lighting. #if defined(_DIRECTIONAL_LIGHT) float4 directionalLightDirection = _WorldSpaceLightPos0; fixed diffuse = max(0.0, dot(worldNormal, directionalLightDirection)); fixed specular = 0.0; #endif fixed3 ibl = unity_IndirectSpecColor.rgb; // Fresnel lighting. #if defined(_FRESNEL) fixed fresnel = 1.0 - saturate(abs(dot(worldViewDir, worldNormal))); #if defined(_RIM_LIGHT) fixed3 fresnelColor = _RimColor * pow(fresnel, _RimPower); #else fixed3 fresnelColor = unity_IndirectSpecColor.rgb * (pow(fresnel, _FresnelPower) * max(_Smoothness, 0.5)); #endif #endif // Final lighting mix. fixed4 output = albedo; fixed3 ambient = glstate_lightmodel_ambient + fixed3(0.25, 0.25, 0.25); fixed minProperty = min(_Smoothness, _Metallic); #if defined(_DIRECTIONAL_LIGHT) fixed oneMinusMetallic = (1.0 - _Metallic); output.rgb = lerp(output.rgb, ibl, minProperty); #if defined(_LIGHTWEIGHT_RENDER_PIPELINE) fixed3 directionalLightColor = _MainLightColor.rgb; #else fixed3 directionalLightColor = _LightColor0.rgb; #endif output.rgb *= lerp((ambient + directionalLightColor * diffuse + directionalLightColor * specular) * max(oneMinusMetallic, _MinMetallicLightContribution), albedo, minProperty); output.rgb += (directionalLightColor * albedo * specular) + (directionalLightColor * specular * _Smoothness); output.rgb += ibl * oneMinusMetallic * _IblContribution; #endif #if defined(_FRESNEL) #if defined(_RIM_LIGHT) output.rgb += fresnelColor; #else output.rgb += fresnelColor * (1.0 - minProperty); #endif #endif #if defined(_EMISSION) output.rgb += _EmissiveColor; #endif return output; }
... #if defined(_FRESNEL) fixed fresnel = 1.0 - saturate(abs(dot(worldViewDir, worldNormal))); #if defined(_RIM_LIGHT) fixed3 fresnelColor = _RimColor * pow(fresnel, _RimPower); #else fixed3 fresnelColor = unity_IndirectSpecColor.rgb * (pow(fresnel, _FresnelPower) * max(_Smoothness, 0.5)); #endif #endif ...
リムライトを使用する場合fresnelColorにプロパティで設定されるRimColorとfresnelのRimPower乗の値が積算されます。
fresnelは worldViewDirとworldNormal(ワールド座標での法線)の内積の絶対値を0~1にクランプ(0以下なら0に、1以上なら1に、0~1の間ならその値がそのまま返されます。)して1から減算された値が代入されます。
worldViewDirにはワールド空間のビュー方向が代入されます。
フレネルが何を意味するかに関してはわかりやすい記事がありました。
周囲の反射を意味するようです。
#if defined(_FRESNEL) #if defined(_RIM_LIGHT) output.rgb += fresnelColor; #else output.rgb += fresnelColor * (1.0 - minProperty); #endif #endif
リムライトは最終的に出力されるoutputにfrenelColorに加算されます。
リムライトとして重要な要素はfresnelで、これに_RimColorが加算され(オーバーレイ)されることでリムライトが表現されます。
試しに次のように書き換えてみました。 ... #if defined(FRESNEL) fixed fresnel = 1.0 - saturate(abs(dot(worldViewDir, worldNormal))); #if defined(RIM_LIGHT) fixed3 fresnelColor = RimColor; //fresnelsを削除した #else fixed3 fresnelColor = unity_IndirectSpecColor.rgb * (pow(fresnel, FresnelPower) * max(_Smoothness, 0.5)); #endif #endif ...
結果は次のようになります。 [f:id:Holomoto-Sumire:20200814100432j:plain] flesnelを使用することでリムライトが表現されます。