Gregory Igehy

Dancing at hemisphere coordinate

Note of importance sampling of GGX

About

  • GGX のスペキュラシェーディングにおける重点サンプリングについて
  • モンテカルロ法を使って, GGX のスペキュラシェーディングによる IBL を求めるのに使う
  • 下の例だと, 1024 個の点をサンプリングしている
    • 仮にミップマップを使ったとしても, 十分なクオリティを出すためには 16 点以上のサンプリングが必要とのこと

f:id:gregory-igehy:20150226153833j:plain

Note

// From NOTES ON IMPORTANCE SAMPLING @ AUGMENTED PIXELS

vec2 importance_sample_ggx( vec2 xi )
{
  float phi   = 2.0f * PI * xi.x;
  float theta = acos( sqrt( ( 1.0f - xi.y )/
                            ( ( a * a - 1.0f ) * xi.y + 1.0f )
                           ) );
  return vec2( phi, theta );
}
  • 以下は "Real Shading in Unreal Engine 4" のコースノートから引用してコメントをつけたもの
// GGX の重点サンプリングをする関数で, ハーフベクトルを返す
float3 ImportanceSampleGGX( float2 Xi, float Roughness , float3 N )
{
    // α = ラフネス^2
    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 );

    return TangentX * H.x + TangentY * H.y + N * H.z;
}

// リファレンスとしての GGX の重点サンプリング
float3 SpecularIBL( float3 SpecularColor, float Roughness, float3 N, float3 V )
{
    float3 SpecularLighting = 0;

    // サンプリングするためのループ
    const uint NumSamples = 1024;
    for( uint i = 0; i < NumSamples; i++ )
    {
        // Hammersley サンプリングによる乱数
        float2 Xi = Hammersley( i, NumSamples );

        // 上の GGX の重点サンプリング
        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;

           // G 項
           float G  = G_Smith( Roughness , NoV, NoL );
           
           // F 項
           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 )
           // 分子と分母で打ち消しあう項を考慮
           SpecularLighting      += SampleColor * F * G * VoH / ( NoH * NoV );
       }
    }

    // サンプル数で除算
    return SpecularLighting / NumSamples;
}