PBR-BRDF-Disney-Unity-1 - 知乎 (zhihu.com)
Principled BRDF
Disney的Principled BRDF模型包含以下十一个参数,它其实涵盖了UE4的多个shading model。
- baseColor:向量,基础颜色。
- metallic:标量,0表示电介质,1表示金属。
- roughness:标量,粗糙度。
- specular:标量,入射镜面反射量,用于取代折射率。
- specularTint:标量,镜面反射颜色,利用该变量和baseColor可以控制镜面反射颜色。
- sheen:标量,光泽度,布料的属性。
- sheenTint:标量,光泽颜色,布料的属性。
- subsurface:标量,使用次表面近似控制漫反射形状。
- clearcoat:标量,clearCoat的属性。
- clearcoatGloss:标量,clearCoat的属性。
- anisotropic:标量,各向异性程度,0表示各向同性,1表示最大各向异性。
准备工作:
vec3 mon2lin(vec3 x)
{
return vec3(pow(x[0], 2.2), pow(x[1], 2.2), pow(x[2], 2.2));
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
float NdotL = dot(N,L);
float NdotV = dot(N,V);
if (NdotL < 0 || NdotV < 0) return vec3(0);
vec3 H = normalize(L+V);
float NdotH = dot(N,H);
float LdotH = dot(L,H);
vec3 Cdlin = mon2lin(baseColor);
float Cdlum = .3*Cdlin[0] + .6*Cdlin[1] + .1*Cdlin[2]; // luminance approx.
vec3 Ctint = Cdlum > 0 ? Cdlin/Cdlum : vec3(1); // normalize lum. to isolate hue+sat
vec3 Cspec0 = mix(specular*.08*mix(vec3(1), Ctint, specularTint), Cdlin, metallic);
vec3 Csheen = mix(vec3(1), Ctint, sheenTint);
// diffuse
// subsurface
// specular
// sheen
// clearcoat
return ((1/PI) * mix(Fd, ss, subsurface)*Cdlin + Fsheen)
* (1-metallic)
+ Gs*Fs*Ds + .25*clearcoat*Gr*Fr*Dr;
}
Diffuse
Diffuse部分用的是Disney自己的模型,考虑到了入射角度、出射角度和粗糙度的影响。
float SchlickFresnel(float u)
{
float m = clamp(1-u, 0, 1);
float m2 = m*m;
return m2*m2*m; // pow(m,5)
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
//....
// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
// and mix in diffuse retro-reflection based on roughness
float FL = SchlickFresnel(NdotL);
float FV = SchlickFresnel(NdotV);
float Fd90 = 0.5 + 2 * LdotH*LdotH * roughness;
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
// ...
}
- Unity和UE引擎的的写法
// Unity UnityStandardBRDF.cginc 文件内
// 注意:迪士尼漫反射必须乘以diffuse Albedo / PI。 这是在此功能之外完成的。
half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
{
half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
// 两个schlick菲涅耳术语
half lightScatter = (1 + (fd90 - 1) * Pow5(1 - NdotL));
half viewScatter = (1 + (fd90 - 1) * Pow5(1 - NdotV));
return lightScatter * viewScatter;
}
//BRDF1_Unity_PBS
// DisneyDiffuse 项
half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;
// 在这里乘了 nl
// UE4 BRDF.ush文件内
// [Burley 2012, "Physically-Based Shading at Disney"]
float3 Diffuse_Burley( float3 DiffuseColor, float Roughness, float NoV, float NoL, float VoH )
{
float FD90 = 0.5 + 2 * VoH * VoH * Roughness;
float FdV = 1 + (FD90 - 1) * Pow5( 1 - NoV );
float FdL = 1 + (FD90 - 1) * Pow5( 1 - NoL );
return DiffuseColor * ( (1 / PI) * FdV * FdL );
}
Specular D
- Specular部分分别采用了法向分布函数GGX、遮挡项SmithGGX、菲涅尔项Schlick,并且直接计算的是各向异性的结果。
- Unity实现与迪斯尼公式的有些不同:DFG( 迪斯尼) ---->> DFV(unity) ,把G项替换成了V项,但整体框架一样。在unity URP LitShader的直接光照部分里有祥细的注释说明。
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
return 1 / (PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH ));
}
float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay)
{
return 1 / (NdotV + sqrt( sqr(VdotX*ax) + sqr(VdotY*ay) + sqr(NdotV) ));
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
//....
// specular
float aspect = sqrt(1-anisotropic*.9);
float ax = max(.001, sqr(roughness)/aspect);
float ay = max(.001, sqr(roughness)*aspect);
float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay);
float FH = SchlickFresnel(LdotH);
vec3 Fs = mix(Cspec0, vec3(1), FH);
float Gs;
Gs = smithG_GGX_aniso(NdotL, dot(L, X), dot(L, Y), ax, ay);
Gs *= smithG_GGX_aniso(NdotV, dot(V, X), dot(V, Y), ax, ay);
// ...
}
- Unity和UE
// UnityBuiltin UnityStandardBRDF.cginc 文件内
inline float GGXTerm (float NdotH, float roughness)
{ //对应迪斯尼GTR2 下层
float a2 = roughness * roughness;
float d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad
return UNITY_INV_PI * a2 / (d * d + 1e-7f); //此功能不适合在Mobile上运行,
//因此epsilon小于一半
}
// UE4 BRDF.ush文件内 Anisotropic GGX
// [Burley 2012, "Physically-Based Shading at Disney"]
float D_GGXaniso( float ax, float ay, float NoH, float3 H, float3 X, float3 Y )
{
float XoH = dot( X, H );
float YoH = dot( Y, H );
float d = XoH*XoH / (ax*ax) + YoH*YoH / (ay*ay) + NoH*NoH;
return 1 / ( PI * ax*ay * d*d );
}
float GTR1(float NdotH, float a)
{
if (a >= 1) return 1/PI;
//如果a大于等于1,就返回圆周率倒数
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return (a2-1) / (PI*log(a2)*t);
}
float GTR2(float NdotH, float a)
{
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return a2 / (PI * t*t);
}
//各项异性版本
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
return 1 / (PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH ));
}.,.
Subsurface
次表面散射项的计算公式与Diffuse类似,除了F90用的是LdotH*LdotH*roughness。计算出来的Fss会经过remapping再跟Diffuse项以系数subsurface混合。
公式:
代码:
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
//....
// Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf
// 1.25 scale is used to (roughly) preserve albedo
// Fss90 used to "flatten" retroreflection based on roughness
float Fss90 = LdotH*LdotH*roughness;
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
float ss = 1.25 * (Fss * (1 / (NdotL + NdotV) - .5) + .5);
// ...
}
Sheen
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
//....
// sheen
vec3 Fsheen = FH * sheen * Csheen;
// ...
}
ClearCoat
float GTR1(float NdotH, float a)
{
if (a >= 1) return 1/PI;
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return (a2-1) / (PI*log(a2)*t);
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
//....
// clearcoat (ior = 1.5 -> F0 = 0.04)
float Dr = GTR1(NdotH, mix(.1,.001,clearcoatGloss));
float Fr = mix(.04, 1.0, FH);
float Gr = smithG_GGX(NdotL, .25) * smithG_GGX(NdotV, .25);
// ...
}
analytic
# Copyright Disney Enterprises, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License
# and the following modification to it: Section 6 Trademarks.
# deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the
# trade names, trademarks, service marks, or product names of the
# Licensor and its affiliates, except as required for reproducing
# the content of the NOTICE file.
#
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# variables go here...
# [type] [name] [min val] [max val] [default val]
::begin parameters
color baseColor .82 .67 .16
//固有色
float metallic 0 1 0
//金属度(金属(0 = 电介质,1 =金属,0-1之间半导体)非特殊需要切勿使用半导体调效果
float subsurface 0 1 0
//次表面,控制漫反射形状
float specular 0 1 .5
//镜面反射强度,取代折射率
float roughness 0 1 .5
//粗糙度,控制漫反射和镜面反射
float specularTint 0 1 0
//镜面反射颜色,物理向美术的让步
float anisotropic 0 1 0
//各向异性,高光的纵横比
float sheen 0 1 0
//光泽度,用于布料
float sheenTint 0 1 .5
//光泽颜色,sheen颜色控制
float clearcoat 0 1 0
//清漆,第二层高光
float clearcoatGloss 0 1 1
//清漆光泽度,0 = “缎面(satin)”,1 = “光泽(gloss)”
::end parameters
::begin shader
const float PI = 3.14159265358979323846;
//圆周率
float sqr(float x) { return x*x; }
//平方
float SchlickFresnel(float u)
{//SchlickFresnel 菲涅耳
float m = clamp(1-u, 0, 1);
//反向,并限制在0-1范围内
float m2 = m*m;
//平方
return m2*m2*m; // 5次方
}
float GTR1(float NdotH, float a)
{
if (a >= 1) return 1/PI;
//如果a大于等于1,就返回圆周率倒数
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return (a2-1) / (PI*log(a2)*t);
}
float GTR2(float NdotH, float a)
{
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return a2 / (PI * t*t);
}
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
return 1 / (PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH ));
}
float smithG_GGX(float NdotV, float alphaG)
{
float a = alphaG*alphaG;
float b = NdotV*NdotV;
return 1 / (NdotV + sqrt(a + b - a*b));
}
float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay)
{
return 1 / (NdotV + sqrt( sqr(VdotX*ax) + sqr(VdotY*ay) + sqr(NdotV) ));
}
vec3 mon2lin(vec3 x)
{
return vec3(pow(x[0], 2.2), pow(x[1], 2.2), pow(x[2], 2.2));
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
float NdotL = dot(N,L);
float NdotV = dot(N,V);
if (NdotL < 0 || NdotV < 0) return vec3(0);
//限制条件,如果小于0,则无意义
vec3 H = normalize(L+V);
float NdotH = dot(N,H);
float LdotH = dot(L,H);
vec3 Cdlin = mon2lin(baseColor); //线性空间
float Cdlum = .3*Cdlin[0] + .6*Cdlin[1] + .1*Cdlin[2]; // luminance approx.
vec3 Ctint = Cdlum > 0 ? Cdlin/Cdlum : vec3(1); // normalize lum. to isolate hue+sat
vec3 Cspec0 = mix(specular*.08*mix(vec3(1), Ctint, specularTint), Cdlin, metallic); //高光强度线性插值,
vec3 Csheen = mix(vec3(1), Ctint, sheenTint); //sheen强度的计算方式
// Diffuse fresnel - 从正常入射的1到放牧的0.5
// 并根据粗糙度混合漫反射回射
float FL = SchlickFresnel(NdotL), FV = SchlickFresnel(NdotV);
float Fd90 = 0.5 + 2 * LdotH*LdotH * roughness;
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
//基于各向同性bssrdf的Hanrahan-Krueger brdf逼近
// 1.25刻度用于(大致)保留反照率
// Fss90用于“平整”基于粗糙度的回射
float Fss90 = LdotH*LdotH*roughness;
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
float ss = 1.25 * (Fss * (1 / (NdotL + NdotV) - .5) + .5);
// specular
float aspect = sqrt(1-anisotropic*.9);
float ax = max(.001, sqr(roughness)/aspect);
float ay = max(.001, sqr(roughness)*aspect);
float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay);
float FH = SchlickFresnel(LdotH);
vec3 Fs = mix(Cspec0, vec3(1), FH);
float Gs;
Gs = smithG_GGX_aniso(NdotL, dot(L, X), dot(L, Y), ax, ay);
Gs *= smithG_GGX_aniso(NdotV, dot(V, X), dot(V, Y), ax, ay);
// sheen
vec3 Fsheen = FH * sheen * Csheen;
// clearcoat (ior = 1.5 -> F0 = 0.04)
float Dr = GTR1(NdotH, mix(.1,.001,clearcoatGloss));
float Fr = mix(.04, 1.0, FH);
float Gr = smithG_GGX(NdotL, .25) * smithG_GGX(NdotV, .25);
return ((1/PI) * mix(Fd, ss, subsurface)*Cdlin + Fsheen)
* (1-metallic)
+ Gs*Fs*Ds + .25*clearcoat*Gr*Fr*Dr;
}
::end shader