GIT地址:

https://github.com/lingzerg/LingzergDemo ​ github.com

PBR中一个比较典型的实现-BRDF, 就是表达一束光照射在一个表面(微面元)上之后反射出去的结果

BRDF有三个参数构成了整个运算的基础

F0, baseColor , 而金属度决定更靠近那个值

F0就是反射率, 当我们90度直视一个表面的时候, 看到的光子回弹的比例

unity中无法设置这个F0, 少了一个控制的维度, 可能需要我们手动添加, 后面我们会在代码中添加这个

PBR方程/BRDF函数

BRDF可以看成两个函数

kd, ks

DGF的公式我们先看一眼:

PBR那个公式L 则是整个半球的辐照度积分

DOT – 点积

```Shader "Unlit/MyBRDF"
{
Properties
{
_Color ("Base Color", Color) = (1,1,1,1)
[Gamma] _Metallic ("Metallic", Range(0,1)) = 0.5
_Roughness ("Roughness", Range(0,1)) = 0.5
_BaseF0 ("BaseF0",Range(0,1)) = 0.04
}
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityStandardBRDF.cginc"

#define PI 3.14159274f
struct VertexInput
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 uv : TEXCOORD0;

};
struct Interpolators
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
fixed3 normal : TEXCOORD2;

};
fixed4 _Color;
fixed _Metallic,_Roughness,_BaseF0;
Interpolators vert (VertexInput v)
{
Interpolators i;
i.vertex = UnityObjectToClipPos(v.vertex);
i.worldPos = mul(unity_ObjectToWorld, v.vertex);

i.normal = UnityObjectToWorldNormal(v.normal);
i.normal = normalize(i.normal);
return i;
}
fixed4 frag (Interpolators i) : SV_Target
{

return 0;
}
ENDCG
}
}
}```

F的公式:

```//F项 fresnel
fixed3 fresnelSchlick(float cosTheta, fixed3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}```

```fixed3 F0 = _BaseF0;
F0 = lerp(F0, _Color.rgb, _Metallic);
fixed3 F = fresnelSchlick(VdotH, F0);```

```fixed4 frag (Interpolators i) : SV_Target
{
fixed4 FinalColor = 0;
float3 lightColor = _LightColor0.rgb;
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

float3 normal = normalize(i.normal);
float VdotH = max(saturate(dot(viewDir, halfVector)), 0.000001);
float NdotL = max(saturate(dot(normal, lightDir)), 0.000001);
fixed3 F0 = _BaseF0;
F0 = lerp(F0, _Color.rgb, _Metallic);
fixed3 F = fresnelSchlick(VdotH, F0);

fixed kd = (1-F)*(1-_Metallic);
float3 diffuse = _Color/PI * kd;
FinalColor.rgb = diffuse * lightColor * NdotL;
FinalColor.a;
return FinalColor;
}```

`return FinalColor * PI;`

```float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
float3 halfVector = normalize(lightDir + viewDir);  //半角向量```

```//D项
fixed DistributionGGX(fixed3 NdotH, fixed a) {
fixed a2 = a*a;
fixed denom = (NdotH*NdotH * (a2-1)+1);
denom = PI * denom * denom;
return a2/denom;
}```

```fixed D = DistributionGGX(NdotH,roughness);
return D;```

```//G项
fixed SchlickGGX(float cosTheta, fixed k) {
return cosTheta/(cosTheta* (1-k)+k);
}```

`fixed k_dir = pow((squareRoughness+1),2)/8;`

```fixed ggx1 = SchlickGGX(NdotL,k_dir);
fixed ggx2 = SchlickGGX(NdotV,k_dir);
fixed G = ggx1 * ggx2;
return G;```

`return fixed4(F*G*D,1);`

`FDG /= 4*NdotV*NdotL;`

TC130：彻底看懂PBR/BRDF方程 ​ zhuanlan.zhihu.com

```FinalColor.rgb = diffuse;
FinalColor.rgb += +FDG;

FinalColor.rgb *=  lightColor * NdotL * PI;
FinalColor.a = 1;
return FinalColor;```

`float3 F = F0 + (1 - F0) * exp2((-5.55473 * vh - 6.98316) * vh);`

unity肯定也会有一定的改动, 所以略有出入并不要紧,并且我觉得我实现的效果更接近公式的效果,毕竟unity是一个需要结果乘π的引擎…

https://learnopengl.com/PBR/Theory ​ learnopengl.com