Shader - PBR BRDF

参考Unity的Standard Shader直接写个BRDF PBS的fragment shader,作为基础方便以后修改。

v2f结构

1
2
3
4
5
6
7
8
struct v2f {
float4 pos : SV_POSITION;
half4 pack0 : TEXCOORD0; // _MainTex _BumpMap
half3 viewDir : TEXCOORD01;
half3 worldPos : TEXCOORD2;
half3x3 tangentToWorld : TEXCOORD3; // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
SHADOW_COORDS(6)
};

顶点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// Vertex
v2f vert (appdata_full v)
{
...

o.pos = UnityObjectToClipPos(v.vertex);
o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.pack0.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);

...

// half3x3(tangent, binormal, normal)
o.tangentToWorld = CreateTangentToWorldPerVertex(worldNormal, worldTangent.xyz, worldTangent.w);

...
}
  • 把uv都pack到TEXCOORD0
  • 在顶点处建立tangentToWorld矩阵

BRDF

UnityStandardBRDF.cginc 的 BRDF1_Unity_PBS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
// The amount we shift the normal toward the view vector is defined by the dot product.
half shiftAmount = dot(normal, viewDir);
normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
// A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
//normal = normalize(normal);

half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
#else
half nv = abs(dot(normal, viewDir)); // This abs allow to limit artifact
#endif

half nl = saturate(dot(normal, lightDir));
half nh = saturate(dot(normal, halfDir));

half lv = saturate(dot(lightDir, viewDir));
half lh = saturate(dot(lightDir, halfDir));

half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;

half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);

#if UNITY_BRDF_GGX
half V = SmithJointGGXVisibilityTerm (nl, nv, roughness);
half D = GGXTerm (nh, roughness);
#else
// Legacy
half V = SmithBeckmannVisibilityTerm (nl, nv, roughness);
half D = NDFBlinnPhongNormalizedTerm (nh, PerceptualRoughnessToSpecPower(perceptualRoughness));
#endif

half specularTerm = V * D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later

#ifdef UNITY_COLORSPACE_GAMMA
specularTerm = sqrt(max(1e-4h, specularTerm));
#endif

// specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value
specularTerm = max(0, specularTerm * nl);
#if defined(_SPECULARHIGHLIGHTS_OFF)
specularTerm = 0.0;
#endif

Metallic & Specular

Metallic

1
2
3
4
5
6
7
8
9
10
11
fixed4 metallicGlossTex = tex2D(_MetallicGlossMap, uv);
half metallic = _Metallic * metallicGlossTex.r;
half smoothness = _Glossiness * metallicGlossTex.a;

half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
half oneMinusReflectivity = oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
fixed3 specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, tex.rgb, metallic);

...

half3 diffColor = tex * oneMinusReflectivity * occ;

Specular

1
2
3
4
5
6
7
8
9
10
fixed4 specTex = tex2D(_SpecGlossMap, uv);
fixed3 specColor = specTex.rgb;
half smoothness = _Glossiness * specTex.a;

half oneMinusReflectivity = 1 - SpecularStrength(specColor);

...

// EnergyConservationBetweenDiffuseAndSpecular
half3 diffColor = tex * (half3(1,1,1) - specColor) * occ;

分享