whoimi

A geek blog

View on GitHub

UE4光照模型

引文

主要参考文章《Real Shading in Unreal Engine 4》by Brian Karis, Epic Games.

详细介绍了UE4的PBR模型,这里对其中主要的细节总结。

UE4的PBR灵感来自于Disney:

Burley, Brent, “Physically-Based Shading at Disney”, part of “Practical Physically Based Shading in Film and Game Production”, SIGGRAPH 2012 Course Notes. http://blog.selfshadow.com/ publications/s2012-shading-course/

使用PBR的目的如下,原文:

Real-Time Performance
     First and foremost, it needs to be efficient to use with many lights visible at a time.
    Reduced Complexity
     There should be as few parameters as possible. A large array of parameters either results in
    decision paralysis, trial and error, or interconnected properties that require many values to be
    changed for a single intended effect.
     We need to be able to use image-based lighting and analytic light sources interchangeably, so
    parameters must behave consistently across all light types.

Intuitive Interface
     We prefer simple-to-understand values, as opposed to physical ones such as index of refraction.
    Perceptually Linear
     We wish to support layering through masks, but we can only afford to shade once per pixel. This
    means that parameter-blended shading must match blending of the shaded results as closely as
    possible.
    
Easy to Master
     We would like to avoid the need for technical understanding of dielectrics and conductors, as well
    as minimize the effort required to create basic physically plausible materials.
    Robust
     It should be difficult to mistakenly create physically implausible materials.
     All combinations of parameters should be as robust and plausible as possible.
    Expressive
     Deferred shading limits the number of shading models we can have, so our base shading model
    needs to be descriptive enough to cover 99% of the materials that occur in the real world.
     All layerable materials need to share the same set of parameters in order to blend between them.
    Flexible
     Other projects and licensees may not share the same goal of photorealism, so it needs to be
    flexible enough to enable non-photorealistic rendering

光照模型

UE4的漫反射模型使用Lambertian:

$ c_{diff} $是表面的albedo。

UE4的高光同样使用微表面模型:

D项 :GGX (Trowbridge-Reitz)

G项:Schlick

UE4使用的Schlick-GGX(用在IBL上)

UE4又根据Disney文章对其做了修改(这个修改只用在解析光源上,IBL在glancing angles会太暗):

F项:Schlick’s approximation

IBL

首先需要解决的是辐射度积分,通常使用重要度采样:

表示采样半球上的所有光线.k表示反射探针上的一个点,采样多个点,p表示概率。

代码:

float3 ImportanceSampleGGX( float2 Xi, float Roughness , float3 N )
{
    float a = Roughness * Roughness;
    float Phi = 2 * PI * Xi.x;
    float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
    float SinTheta = sqrt( 1 - CosTheta * CosTheta );
    float3 H;
    H.x = SinTheta * cos( Phi );
    H.y = SinTheta * sin( Phi );
    H.z = CosTheta;
    float3 UpVector = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
    float3 TangentX = normalize( cross( UpVector , N ) );
    float3 TangentY = cross( N, TangentX );
    // Tangent to world space
    return TangentX * H.x + TangentY * H.y + N * H.z;
}

重要度采样的推导

这部分内容主要解释什么是重要度采样以及如何推导。

从PDF(概率密度函数:积分为1)到重要度采样函数的过程。

目的:通过求和模拟复杂函数积分,代表就是蒙特卡洛积分。

蒙特卡洛积分是等分采样,重要度采样能够修正蒙特卡洛积分的权重来提升准确度。

重要度采样:引入分布p(x)。

蒙特卡洛计算积分:

重要度采样积分:

我们可以将上面的函数当做$ \frac{\pi(x)}{p(x)}f(x) $在概率p(x)上的期望,则:

可以在$ p(x) $上采样估计期望:

则$ \frac{\pi(x_i)}{p(x_i)} $为重要度权重。

所以重要度采样就需要有一个新的分布和分布对应的概率。

Importance Sampling techniques for GGX

针对IBL和ray tracing 没有L方向所以需要这个方法计算。

光照函数:

对于一条光线:

我们现在探讨GGX的重要度采样,应为NDF对于整个BRDF有重要的影响,在Ray Tracing当中也是重要的讨论部分。

为了进行重要度采样,我们需要在求D(h)的边缘分布函数(CDF)的倒数。来生成一个微表面法线(因为光线追踪和IBL当中可能没有光线方向,所以需要特殊的方法计算这个h)。

概率论预备知识

概率密度函数:“PDF”(Probability Density Function)

联合概率:P(A,B)

条件概率:P(A B) = P(A,B) / P(A)

边缘概率:P(A)

事件独立:P(A B) = P(A,B) / P(B) = P(A)

Phong BRDF

Phong模型的PDF:

首先对$ \phi $积分,得到了$ \theta $的边缘密度函数。:

然后推导出$ \phi $的条件概率,各项同性的$ p(\phi   \theta) $结果永远都是这样。刚好是一个单位圆的周长,他们是独立的:

现在我们得到了两个条件概率,分别积分就可以得到具体的概率值。

设 $ P(S_\phi) $为$ \phi $的概率,则对条件概率积分:

假设 $ P(S_\phi) $是一个随机变量:

设 $ P(S_\theta) $为$ \theta $的概率,则对条件概率积分:

$ \xi $是一个随机变量。重要度采样的代码:

vec2 importance_sample_phong(vec2 xi)
{
  float phi = 2.0f * PI * xi.x;
  float theta = acos(pow(1.0f - xi.y, 1.0f/(n+1.0f)));
  return vec2(phi, theta);
}

IBL计算

float3 SpecularIBL( float3 SpecularColor , float Roughness , float3 N, float3 V )
{
    float3 SpecularLighting = 0;
    const uint NumSamples = 1024;
    for( uint i = 0; i < NumSamples; i++ )
    {
    	float2 Xi = Hammersley( i, NumSamples );
        // 使用xi分布进行采样。
        float3 H = ImportanceSampleGGX( Xi, Roughness , N );
        float3 L = 2 * dot( V, H ) * H - V;
        float NoV = saturate( dot( N, V ) );
        float NoL = saturate( dot( N, L ) );
        float NoH = saturate( dot( N, H ) );
        float VoH = saturate( dot( V, H ) );
        if( NoL > 0 )
        {
            float3 SampleColor = EnvMap.SampleLevel( EnvMapSampler , L, 0 ).rgb;
            float G = G_Smith( Roughness , NoV, NoL );
            float Fc = pow( 1 - VoH, 5 );
            float3 F = (1 - Fc) * SpecularColor + Fc;
            // Incident light = SampleColor * NoL
            // Microfacet specular = D*G*F / (4*NoL*NoV)
            // pdf = D * NoH / (4 * VoH) xi对应的概率
            // SpecularLighting =  D*G*F / (4*NoL*NoV) /  (pdf)
            // SpecularLighting = NoL* D*G*F / (4*NoL*NoV) * (4 * VoH)  / D * NoH
            // 这里pdf是1所以公式变为 G*F / (4*NoL*NoV),约分之后得到下面的公式
            // 也就是说P等于1
            SpecularLighting += SampleColor * F * G * VoH / (NoH * NoV);
        }
    }
    // 取平均值
    return SpecularLighting / NumSamples;
}