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;
}