記号 | 説明 |
---|---|
視線方向を表す単位ベクトル | |
入射光の方向を表す単位ベクトル | |
法線方向を表す単位ベクトル | |
ハーフベクトル | |
BRDFの拡散反射成分 | |
拡散反射率(ディフューズアルベド) | |
法線方向から入射する光の反射率 | |
グレージング角から入射する光の反射率 |
Disney Diffuse BRDFとは?
Disney Diffuse BRDFとはBurley Diffuseとも呼ばれ、エネルギー保存の法則を厳密に守っているわけではなく、発案者の経験則に基づいて提案されました。 特徴的なのは、DiffuseモデルにFresnelの効果が組み込まれている点です。
はディフューズアルベドに円周率を除算しています。 これは古典的なランバート反射です。 その後に続くはFresnelのSchlickによる近似式で、以下のような式です。
これを最初の式に代入した式がこちらです。
式の考察
の係数であるフレネル部分について考えていきます。
の値が大きくなるほどフレネルの値が大きくなることは明白です。
roughneesは粗さを表しており、金属に近い質感にしたいほど大きな値をデザイナーが設定します。 金属はフレネル反射が強くなるため、値が大きくなるほど、の値も大きくなるようになっています。
はLightとViewの角度が同一方向の時にHdotLが1となり、角度が離れるほど値が小さくなります。 そのため、拡散反射というよりは、指向性を持った再帰反射モデルに近いものだと思います。
指向性 ・・・太陽光ように表面の輝度がどの角度から見ても一定である拡散反射とは異なり、光源の方向により、表面の輝度が異なる性質
再帰反射・・・反射光が入射光と同一方向に反射する現象
実装
さきほどの公式をシェーダーで書くとこうなります。
Shader "DisneyDiffuseBRDF" { Properties { _Roughness("Roughness", Range(0.0, 1.0)) = 0.5 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; half3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; half3 worldNormal : TEXCOORD2; half3 viewDir : TEXCOORD3; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); o.viewDir = UnityWorldSpaceViewDir(worldPos); return o; } sampler2D _MainTex; float _Roughness; float3 _LightColor0; inline half3 F_Schlick(half3 f0, half3 f90, half cos) { return f0 + (f90 - f0) * pow(1 - cos, 5); } inline half Fd_Burley(half ndotv, half ndotl, half ldoth, half roughness) { half fd90 = 0.5 + 2 * ldoth * ldoth * roughness; half lightScatter = F_Schlick(1,fd90,ndotl); half viewScatter = F_Schlick(1,fd90,ndotv); half diffuse = lightScatter * viewScatter / UNITY_PI; return diffuse; } fixed4 frag (v2f i) : SV_Target { half3 normal = normalize(i.worldNormal); half3 viewDir = normalize(i.viewDir); half ndotv = abs(dot(normal, viewDir)); float ndotl = max(0, dot(normal, _WorldSpaceLightPos0.xyz)); float3 halfDir = normalize(_WorldSpaceLightPos0.xyz + viewDir); half ldoth = max(0, dot(_WorldSpaceLightPos0.xyz, halfDir)); half diffuse = Fd_Burley(ndotv,ndotl,ldoth,_Roughness); return fixed4(diffuse * _LightColor0.rgb,1); } ENDCG } } }