What do you know about this notehttps://zhuanlan.zhihu.com/p/260922654
Time: July to December 2020
Modified: 20201213
Statement: This article is a personal study note. I’ll review it myself and enjoy communicating. If there is infringement, error or different understanding in the article, please leave a message: correct and give advice, and I will correct it in time. My highest respect!
Cognition: micro surface theory, energy conservation theory, etc. (see resources)
There are three notes in PBR series. This is the first of the series: Analysis Based on Disney BRDF algorithm


  • preface
  • Disney BRDF source code and corresponding implementation

PBR: former PBR and current PBR. Front PBR: inconvenient to use, unsatisfactory rendering, non art oriented. Now PBR: algorithm optimization, art oriented, saving time and cost. Find new horizons in subjective (artistic effect) and objective (physical reality). Sacrifice physics to improve artistic effect, or sacrifice artistic effect to improve physical accuracy.
From Disney’s speech and open source, it gives the industry standards: always art oriented (art first); concise core controllable parameters; sharing of material sampling data… And so on (generous sharing, admirable).
As master Peter Drucker said, honor depends on contribution; Look at the contribution to the whole society, the whole human group, and the whole civilization. Disney’s speech and open source have epoch-making significance for physical rendering technology.
The prerequisite knowledge for discussion: color space (see the data manipulation and system notes in common graphics in the previous discussion – color space. The previous notes are the basic knowledge required for the later notes).

Disney BRDF source code

# 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
//Solid colored
float metallic 0 1 0
//Metallicity (metal (0 = dielectric, 1 = metal, semiconductor between 0-1) do not use semiconductor modulation effect unless special needs
float subsurface 0 1 0
//Subsurface, controlling diffuse shape
float specular 0 1 .5
//Specular reflection intensity, instead of refractive index
float roughness 0 1 .5
//Roughness, controlling diffuse and specular reflections
float specularTint 0 1 0
//Specular reflection color, the concession of physics to art
float anisotropic 0 1 0
//Anisotropy, aspect ratio of highlights
float sheen 0 1 0
//Gloss, for cloth
float sheenTint 0 1 .5
//Sheen color control
float clearcoat 0 1 0
//Varnish, second high gloss
float clearcoatGloss 0 1 1
//Gloss of varnish, 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 Fresnel
    float m = clamp(1-u, 0, 1);
              //Reverse and limit to 0-1
    float m2 = m*m;
    return m2*m2*m; //  5th power
float GTR1(float NdotH, float a)
    if (a >= 1) return 1/PI;
    //If a is greater than or equal to 1, the reciprocal of PI is returned
    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);
    //Constraints, if less than 0, are meaningless
    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 Fresnel - from 1 of normal incidence to 0.5 of grazing
    //Diffuse reflectance is mixed according to roughness
    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);
     //Hanrahan Krueger BRDF approximation based on isotropic BSSRDF
     //1.25 scale for (approximate) retained albedo
     //Fss90 is used to "flatten" roughness based retroreflection
    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

Implementation of Disney BRDF decomposition
Route and analysis:
First, data resources, before equation calculation, access the correctness of data.
The matching map resources should be correct. Distinguish between gamma space and linear space data resources. PBR workflow has two directions, specific workflow and metallic workflow. In recent years, metallic workflow has been mainly used in games. The main reason is that channel splitting is conducive to iteration (more intuitive parameters and more accurate numerical calculation), which will save more time and cost. This note is also based on metallic flow. Mapping resources in different workflow directions are also different.
Second, Disney function analysis (source code analysis):


Disney BRDF model equation

Brief description of the equation: Based on the theory of micro surface and energy conservation.

  1. Diffuse, usually assumed to be expressed as a Lambert diffusion term, is a constant value.
  2. The fractional form on the right is specular specular:
    D is the distribution in the normal direction of the micro surface. Specular highlights, specular shapes;
    F is Fresnel reflection coefficient;
    G is the geometric change of micro surface, micro geometric term, shadow and mask (mask) of micro surface, and factor.
    θ H is the angle between normal N and half vector h, ndoth;
    θ D is the “difference” angle between L and H half vector H (or symmetrically V and H), ldoth or vdoth;
    θ L is the incidence angle of L vector relative to normal n, ndotl;
    θ V is the angle of incidence of the V vector relative to the normal n, ndotv.

The diagonal angles of ldoth and vdoth are equal. The dot product is to calculate the cos angle, and the effect is the same. See legend below: 2017 – mirror D observation

The whole function is too complex and huge at first glance. The parameters inside are also complex and confusing. So there is a better way: split it. (the function is diffuse + specular)
Part I: diffuse subsurface scattering: diffuse reflection term (the literature points out that the diffuse reflection term is classified into the subsurface scattering term)
Diffusion model. It is generally assumed that, It is expressed by a constant value of Lambert diffusion (the intensity of diffuse reflection in all directions is the same, so it needs to be divided by π). Therefore, ordinary Lambert is different from BRDF Lambert (the ordinary Lambert lightmode NDL factor is part of the BRDF diffuse reflection equation). Gossip: everything has scattering, and metals are no exception. Only weak calculations are deliberately ignored in the calculation and simulation (energy conservation, only black holes can fully absorb energy without reflecting energy).
Three basic BRDF diffusion models: Lambert, Oren Nayar, Hanrahan Krueger


Disney diffuse





Basecolor surface color (diffcolor calculated according to map metallic information, in metallicsetup)
cosθl NdotL
cosθv NdotV
cos θ D ldoth or vdoth
cos θ D is written in two different ways, one is unity ldoth, the other is Disney ldoth, and the other is UE Voh.

// Unity UnityStandardBRDF. Cginc file
//Note: Disney diffuse must be multiplied by diffuse albedo / PI. This is done outside of this function.
half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
    half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
    //Two Schlick Fresnel terms
    half lightScatter   = (1 + (fd90 - 1) * Pow5(1 - NdotL));
    half viewScatter    = (1 + (fd90 - 1) * Pow5(1 - NdotV));
    return lightScatter * viewScatter;
//Disney diffuse item
    half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;
//I took NL here

// UE4 BRDF. In the ush file
// [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 );

Part II: the right part of the formula (surface reflection: specular reflection term)
The unity implementation is somewhat different from the Disney formula: DFG (Disney) – > > DFV (unity), which replaces the G item with the V item, but the overall framework is the same. There are detailed notes in the direct lighting section of the unity URP litshader.
2.1、D( θ h) Specular D observation (distribution in the normal direction of the micro surface) specular reflection highlight, highlight shape.



Several mirror distributions are suitable for merl chromium. Left: log scale plot of specular peak logarithm θ H (degrees); black = chromium, red = GGX( α= 0.006), green = Beckmann (M = 0.013), blue = blinnphong (n = 12000). Right: spot light response of chromium, GGX and Beckmann Beckmann

Introduced and called generalized Trowbridge Reitz, or GTR.


C scaling constant
α The roughness parameter (commonly known as perceptual roughness map) is between 0-1
θ H is ndh
γ Index value


various γ GTR distribution curve and θ H relationship

In BRDF, GTR 2 (main mirror lobe) is first used for the anisotropic and or isotropic metal or non-metal of the base material (commonly known as the lower highlight); GTR 1 (secondary mirror lobe) is second used for the upper transparent coating material (clearcoat varnish material, commonly known as the upper highlight), which is isotropic and non-metallic. (two layer highlight concept, such as car paint: metal layer + varnish layer, and the thickness of varnish is included in the calculation. The varnish is on the top and the metal is on the bottom, and both layers have different highlight effects. Development trend: more complex multi-layer highlight calculation)

// UnityBuiltin UnityStandardBRDF. Cginc file
inline float GGXTerm (float NdotH, float roughness)
{// corresponding to Disney GTR2 lower layer
    float a2 = roughness * roughness;
    float d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad
    return UNITY_ INV_ PI * a2 / (d * d + 1e-7f); // This feature is not suitable for running on mobile,
                                             //Therefore, epsilon is less than half

// UE4 BRDF. Anisotropic GGX in ush file
// [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 );

Disney GTR (gtr1 is γ= 1. GTR2 is γ= 2)

float GTR1(float NdotH, float a)
    if (a >= 1) return 1/PI;
    //If a is greater than or equal to 1, the reciprocal of PI is returned
    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);

//Different versions
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 ));

2017 – mirror D observation is used to calculate how many proportions (represented by circles in the figure below) the normal of micro facets meet M = h. When the conditions are met, the micro surface will reflect light from L to V, and the m-Term vector will be written between notes.

2017-brdf ignores the distance between incoming and outgoing rays. The definition of the size of the circle area is very important in theoretical calculation (calculate the proportion of micro facets). The sampling range is directly linked to the performance

2017-bssrdf calculation of incoming and outgoing ray distance

Set the size of the circle as the judgment value to determine the accuracy of rendering.


2.2、F( θ d) Mirror f observation (Fresnel reflection)



F0 specular reflectance, obtained by LERP (constant value, albedo, metallic). The constant value is directly related to the data color space. The color space is different and the value is different.
cos θ There are two different ways to write D, one is HDL (unity ldoth, Disney ldoth) and the other is HDV (UE Voh).
Gossip: everything has a Fresnel reflex.



Fresnel reflex on the left
//Dot (V, H) is used in the formula. By default, unity passes in dot (L, H)
//Because a large number of calculations of BRDF use the dot product of L and h, and H is the half angle vector of L and V, the included angle of LH and VH is the same.
//You don't need one more variable.
// UnityBuiltin UnityStandardBRDF. Cginc file
Inline half pow5 (half x) // performance optimization
    return x*x * x*x * x;
Inline half3 fresnelterm (half3 F0, half COSA) // corresponding to Disney f
    half t = Pow5 (1 - cosA);   //  Ala Schlick interpolation
    //Dot (V, H) is used in the formula. By default, unity passes in dot (L, H)
    //Because a large number of calculations of BRDF use the dot product of L and h, and H is the half angle vector of L and V, the included angle of LH and VH is the same.不需要多来一个变量。
    return F0 + (1-F0) * t;

// UE4 BRDF. In the ush file
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
float3 F_Schlick( float3 SpecularColor, float VoH )
    float Fc = Pow5( 1 - VoH );                 // 1 sub, 3 mul
    //return Fc + (1 - Fc) * SpecularColor;     // 1 add, 3 mad

    // Anything less than 2% is physically impossible and is instead considered to be shadowing
    return saturate( 50.0 * SpecularColor.g ) * Fc + (1 - Fc) * SpecularColor;


float3 F_Fresnel( float3 SpecularColor, float VoH )
    float3 SpecularColorSqrt = sqrt( clamp( float3(0, 0, 0), float3(0.99, 0.99, 0.99), SpecularColor ) );
    float3 n = ( 1 + SpecularColorSqrt ) / ( 1 - SpecularColorSqrt );
    float3 g = sqrt( n*n + VoH*VoH - 1 );
    return 0.5 * Square( (g - VoH) / (g + VoH) ) * ( 1 + Square( ((g+VoH)*VoH - 1) / ((g-VoH)*VoH + 1) ) );

2.3、G( θ l, θ v) Mirror g microscopic surface geometric change, microscopic geometric term, shadow of microscopic surface and mask (mask) (unity V term is equivalent) is the most complex term



Albedo map of merl 100 material. Left: 50 smooth materials; Right: 50 rough materials.

Compare the albedo maps of multiple specular g models. All plots use the same D (GGX / TR) and f factors. Left: smooth surface( α= 0.02); Right: rough surface( α= 0.5)。 “No G” mode excludes g and 1 / cos θ lcos θ V factor

Three rough distribution models: Kurt’s empirical model (data fitting), Walter’s Smith g derivative, and Schlick’s simpler derivative roughness as a free parameter


This text section corresponds to the illustration (microscopic shadow and mask (mask)), and only calculates the reflection model once. Some rays have incident and no exit examples. Only one effective reflection is calculated

The more complex algorithm in this paper, multiple reflection calculation, is also a general baking use
// UnityBuiltin UnityStandardBRDF. Cginc file
//Low effect version V item, but better performance
//Note: the visibility term is the complete form of Torrance sparrow model, including geometric terms: v = g / (N.L * N.V)
//This makes it easier to swap geometry items and has more optimization space (except perhaps in the case of the cooktorrance geom item)
//General Smith Schlick visibility terminology
inline half SmithVisibilityTerm (half NdotL, half NdotV, half k)
    half gL = NdotL * (1-k) + k;
    half gV = NdotV * (1-k) + k;
    return 1.0 / (gL * gV + 1e-5f); // This feature is not suitable for running on mobile,
                                     //Therefore, epsilon is less than a value that can be expressed as half
//Smith Schlick is Beckman's work
inline half SmithBeckmannVisibilityTerm (half NdotL, half NdotV, half roughness)
    half c = 0.797884560802865h; // c = sqrt(2 / Pi)
    half k = roughness * c;
    return SmithVisibilityTerm (NdotL, NdotV, k) * 0.25f; // *  0.25 is 1 / 4 of the visibility item

//High effect version V item
// Ref:  http://jcgt.org/published/0003/02/03/paper.pdf 2014 literature
//Calculation of visibility items, including geometric functions and trim coefficients
inline float SmithJointGGXVisibilityTerm (float NdotL, float NdotV, float roughness)
    #If 0 // it is closed by default. Note: This is frostbite's GGX Smith joint scheme (accurate, but it requires two prescriptions, which is uneconomical)
        //Original recipe:
        //  lambda_v    = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;
        //  lambda_l    = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;
        //  G           = 1 / (1 + lambda_v + lambda_l);
        //Reorder the code to make it more optimized
        half a          = roughness;
        half a2         = a * a;
        half lambdaV    = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);
        half lambdaL    = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
        //Simplified visibility terms: (2.0f * ndotl * ndotv) / ((4.0f * ndotl * ndotv) * (lambda_v + lambda_l + 1e-5f));
        return 0.5f / (lambdaV + lambdaL + 1e-5f);  // This feature is not suitable for running on mobile,
                                                    //Therefore, epsilon is less than a value that can be expressed as half
        //Take this part
        //Approximate value (simplified sqrt, mathematically incorrect, but close enough)
        //This part is the GGX Smith joint approximation scheme of repawn entertainment
        float a = roughness;
        float lambdaV = NdotL * (NdotV * (1 - a) + a);
        float lambdaL = NdotV * (NdotL * (1 - a) + a);
        #if defined(SHADER_API_SWITCH)
            return 0.5f / (lambdaV + lambdaL + 1e-4f); // Solution to hlslcc rounding error
            return 0.5f / (lambdaV + lambdaL + 1e-5f);

// UE
//Smith GGX item g, same sex version
float smithG_GGX(float NdotV, float alphaG)
    float a = alphaG * alphaG;
    float b = NdotV * NdotV;
    return 1 / (NdotV + sqrt(a + b - a * b));
//Smith GGX item g, version of each item
// Derived G function for GGX
float smithG_GGX_aniso(float dotVN, float dotVX, float dotVY, float ax, float ay)
    return 1.0 / (dotVN + sqrt(pow(dotVX * ax, 2.0) + pow(dotVY * ay, 2.0) + pow(dotVN, 2.0)));
//GGX varnish geometry
// G GGX function for clearcoat
float G_GGX(float dotVN, float alphag)
        float a = alphag * alphag;
        float b = dotVN * dotVN;
        return 1.0 / (dotVN + sqrt(a + b - a * b));

2.31、1/4cos θ lcos θ V derivative of micro geometric term, a correction factor used to correct the difference in the number of micro surfaces to macro surfaces (as a whole)


Most physically feasible models that are not specifically described in the form of micro surface can still be described by micro surface model, because they also have distribution function, Fresnel factor and some other factors that can be considered as geometric shadow factor. The only real difference is whether explicit correction factors are included between the micro surface model and other models:


Explicit correction factor

The explicit correction factor is derived from the micro surface model. For models that do not include this factor, use trim: Hidden shadows can be multiplied by 4 cos by dividing the model by D and F θ lcos θ V to determine the factor.
Unity V term (equivalent to G term): BRDF function fitting optimization term
See the description in the fragment directbdrf function in pbr-brdf-disney-unity-3.
2.4. Cloth; 2.5. Rainbow (rainbow can give art a colorful Black: P, skip, have time to further study)

Required References:
Substance PBR instruction manual
Eight monkey PBR instruction manual
Siggraph 2012 original article: 2012 physically based shading at Disney
My sorted Chinese version:
Siggraph 2017 original article: 2017 reflection models (BRDF)
Yan Lingqi (Yan Shen):
I hope there are also friends learning 101102201202 to discuss communication:)
Personal learning notes address:
Hairy Nebula:https://zhuanlan.zhihu.com/p/60977923
Xiong Xinke: source code analysis Chapter 10 Chapter 11
Feng Lele: introduction essentials Chapter 18