简单根据公式实现了一下基于物理的着色,坑的地方还是有不少,单纯的公式搬过来效果会不正确,这里根据网上资料以及自己调整稍微修改了一下部分计算。高光项:Cook-Torrance,漫反射项:Disney
左边为实现的效果,右边为Unity2017自带Standard
shader部分:
Shader "Unlit/BRDF2"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Roughness("Roughness",Range(0.0,1.0)) = 0.5
_F0("F0",range(0.0,10.0)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" }
LOD 100
Pass
{
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#define pi 3.1415926
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldNormal:TEXCOORD1;
float3 worldPos:TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _F0;
float _Roughness;
//BRDF光照模型Cook-Torrance
//specular(V,L)=F(H,L)*(G(V,L,H)*D(H))/(4*(NdotV)*(NdotL))
//F:菲涅尔因子,反射光线的强度和颜色与入射角度之间的关系函数。
//D:微平面分布函数,返回某一给定方向微平面比例。
//G:几何遮罩,微平面的自屏蔽情况。
//V:视角方向
//L:光源方向
//H:中间向量=(V+L)/2
//N:法线向量
//F项:Schlick公式
//F(V,H)=F0+(1-F0)*pow((1-(VdotH)),5)
//F0表示光线方向和法线方向一致时的折射率,你可以直接把它当做一个菲涅尔的调节值。
float FresnelSchlick(float F0,float3 lightDir,float3 halfDir) {
return F0 + (1 - F0)*pow((1 - saturate(dot(lightDir, halfDir))), 5);
}
//D项:GGX分布函数
//D(H)=a*a/(pi*pow((pow(NdotH,2)*(a*a-1)+1),2))
//a为粗糙度=roughness*roughness
float DistributionGGX(float roughness,float3 normalDir,float3 halfDir) {
float a = roughness * roughness;
float a2 = a * a;
return a2 / (pi*pow(pow(saturate(dot(normalDir, halfDir)), 2)*(a2 - 1) + 1, 2));
}
//G项:Smith-Schlick方程
//k=pow((Roughness+1),2)*0.125
//G1(V)=NdotV/(NdotV*(1-k)+k)
//G(L,V,N)=G1(L)*G1(V)
float GeometrySmithSchlick(float roughness,float3 lightDir,float3 viewDir,float3 normalDir) {
float k = pow((roughness + 1), 2)*0.125;
float GL = saturate(dot(lightDir, normalDir)) / (saturate(dot(lightDir, normalDir))*(1 - k) + k);
float GV = saturate(dot(normalDir, viewDir)) / (saturate(dot(normalDir, viewDir))*(1 - k) + k);
return GL * GV;
}
//specular(V,L)=F(H,L)*(G(V,L,H)*D(H))/(pi*(NdotV)*(NdotL))
float3 CookTorranceSpecular(float F0, float roughness, float3 viewDir, float3 lightDir, float3 halfDir, float3 normalDir) {
float F = FresnelSchlick(F0, lightDir, halfDir);
float D = DistributionGGX(roughness, normalDir, halfDir);
float G = GeometrySmithSchlick(roughness, lightDir, viewDir, normalDir);
//float3 specular = F*D*G/ (pi*dot(normalDir, lightDir)*dot(normalDir, viewDir));
//Unity5.3.7中,实际使用的公式,并没有除以(n·l)(n·v),只除以4并且乘以π
float3 specular = F*D* G * pi*0.25;
return specular;
}
//Disney Diffuse
//Fdiffuse=(baseColor/pi)*(1+(FD90-1)*pow((1-NdotL),5))(1+(FD90-1)*pow((1-NdotV),5))
//FD90=0.5+2*roughness*pow((HdotL),2)
//Unity5.3.7中,实际使用的公式,最终并没有除以π
float DisneyDiffuse(float roughness,float3 halfDir,float3 lightDir,float3 normalDir,float3 viewDir) {
float FD90 = 0.5 + 2 * roughness*pow(saturate(dot(halfDir, lightDir)), 2);
//float3 Fdiffuse = float3(1,1,1)*(1 + (FD90 - 1)*pow((1 - dot(normalDir, lightDir)), 5)*(1 + (FD90 - 1)*pow((1 - dot(normalDir, viewDir)),5));
float Fdiffuse = (1 + (FD90 - 1)*pow((1 - saturate(dot(normalDir, lightDir))), 5));
Fdiffuse *= (1 + (FD90 - 1)*pow((1 - saturate(dot(normalDir, viewDir))), 5));
return Fdiffuse;
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = UnityObjectToWorldNormal(v.normal).xyz;
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
float4 frag (v2f i) : SV_Target
{
float3 normalDir = normalize(i.worldNormal);
float3 lightDir =normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float3 halfDir = normalize(lightDir + viewDir);
float3 diffuse = DisneyDiffuse(_Roughness, halfDir, lightDir, normalDir, viewDir);
//考虑光线反方向和法线的夹角余弦
diffuse *= saturate(dot(normalDir, lightDir));
//调整背光面颜色
diffuse = saturate(diffuse) + 0.5;
float4 col = tex2D(_MainTex, i.uv);
float3 specular = CookTorranceSpecular(_F0, _Roughness, viewDir, lightDir, halfDir, normalDir);
return float4(col.rgb*diffuse+specular,1.0);
}
ENDCG
}
}
}