我的博客和笔记我的博客和笔记
首页
文章
  • TurboLink
  • TinyEncrypt
  • UnrealStyleGuide
  • AxTrace
  • Cyclone
  • 数学相关
  • 图形学
  • 密码学
  • 编程语言
关于
GitHub
首页
文章
  • TurboLink
  • TinyEncrypt
  • UnrealStyleGuide
  • AxTrace
  • Cyclone
  • 数学相关
  • 图形学
  • 密码学
  • 编程语言
关于
GitHub
  • 我的文章

    • 从抛币协议到智能合约

      • Part1
      • Part2
    • JPEG算法解密

      • Part1
      • Part2
      • Part3
      • Part4
      • Part5
      • Github
    • SPH算法简介

      • Part1
      • Part2
      • Part3
      • Part4
      • Github
    • 赌博中的数学:Martingle策略
    • 如何生成一个随机的圆形
    • 一个简单的DH密钥协商算法的实现
    • 如何计算线段和圆的交点
    • 一道数学趣题
    • 斐波那契数列和1/89
    • 匀速贝塞尔曲线运动的实现

      • Part1
      • Part2
  • 开源项目

    • TurboLink
    • TinyEncrypt
    • UnrealStyleGuide
    • AxTrace
    • Cyclone
  • 学习笔记

    • 数学相关

      • 常用数学符号
      • 群
      • 数论(一)
      • 数论(二)
      • 数论(三)
      • 概率
    • 密码学

      • RSA
      • 抛币协议
      • 智能扑克协议
    • 图形学

      • 数学基础

        • 矢量
        • 矩阵
        • 立体角
        • 几何变换(一)
        • 几何变换(二)
        • 法线变换
        • 摄像机变换
      • 光照模型

        • 传统光照模型
        • 光度学
        • 双向反射分布函数(BRDF)
        • 微平面理论(一)
        • 微平面理论(二)
        • 微平面理论(三)
        • 光照方程
      • 环境光渲染

        • 环境光渲染(一)
        • 环境光渲染(二)
          • 3. 高光部分
            • 3.1 原理
            • 3.2 预渲染环境光贴图(pre-filtered environment map)
            • 3.3 预渲染贴图的生成
          • 3.3 BRDF部分
          • 3.4 BRDF查找贴图
            • 3.4.1 BRDF贴图的生成
    • 编程语言

      • JavaScript

        • 环境搭建
        • 基本语法
        • 函数
        • 对象和类

环境光渲染(二)


3. 高光部分

3.1 原理

光照方程中的高光部分

Los(ωo→)=∫Ωfs(ωi→,ωo→)Li(ωi→)(ω→i⋅n→)dωi

同样使用蒙特卡洛进行近似积分

∫Ωfs(ωi→,ωo→)Li(ωi→)(ω→in→)dωi≈1N∑k=1Nfs(ωk→,ωo→)Li(ωk→)(ωk→⋅n→)pdf(ωk→)

EPIC在此基础上提出了一种近似计算方法

1N∑k=1Nfs(ωk→,ωo→)Li(ωk→)(ωk→⋅n→)pdf(ωk→,ωo→)≈(1N∑k=1NLi(ωk→))(1N∑k=1Nfs(ωk→,ωo→)(ωk→⋅n→)pdf(ωk→))

这种方法的好处是可以通过分开预计算,尽量减少实际渲染时的计算量,这个公式可以理解为“Env. lighting x BRDF”,左边的第一部分是小平面受到的所有环境光的采样值,第二部分只跟材质的BRDF属性相关。

3.2 预渲染环境光贴图(pre-filtered environment map)

第一个拆分项只和环境光照以及采样点有关,首先采样点集中在ω→r周围(ω→r是ω→o相对于法线n→的反射向量),并且粗糙度越大,采样点越分散。 实际应用中,这个拆分项是通过把环境光贴图预先渲染到一个环境贴图中实现的,称为为"pre-filter map"或者"radiance map",最终渲染时根据ω→r,以及把粗糙度(0~1)转换为mip获得该方向的数值

1N∑k=1NLi(ωk→)=TexCubeLod(PrefilterMap,ω→r,mip)

3.3 预渲染贴图的生成

生成这张贴图时,首先假定n→=ωo→=ωr→,也就是摄像机从法线方向垂直观察小平面,然后开始采样

再最终渲染时,当n→和ωo→不等时,采样点的分布会有一定差异,但基本上可以忽略

3.3.1 采样点的生成

根据前面章节的分析,微表面的法线分布可以用法线分布函数D(h)描述,并且符合

∫ΩD(h)cos⁡θhdω=1

由于微表面法线的概率密度函数pdf(h)符合以下公式

∫Ωpdf(h)=1

所以

pdf(h)=D(h)cos⁡θh

用极坐标表示

pdf(θ,ϕ)=D(h)cos⁡θsin⁡θ

对于各向同性系统,pdf(θ,ϕ)和ϕ无关,所以

pdf(ϕ)=12πpdf(θ)=∫02πpdf(θ,ϕ)dϕ=2π⋅pdf(θ,ϕ)

D函数使用GGX函数,也就是

D(h)=α2π(cos2⁡θ(α2−1)+1)2

其中α=roughness2,此时

pdf(θ)=2π⋅pdf(θ,ϕ)=2α2cos⁡θsin⁡θ(cos2⁡θ(α2−1)+1)2

计算θ的概率分布函数

cdf(θh)=∫0θhpdf(θ)dθ=∫0θh2α2cos⁡θsin⁡θ(cos2⁡θ(α2−1)+1)2dθ

使用Mathmatica计算可得

cdf(θ)=sin2⁡θcos2⁡θ(α2−1)+1=1−cos2⁡θcos2⁡θ(α2−1)+1

根据前面的概率知识,可以得到微表面的法线的球坐标θ和ϕ的采样函数为

θ=cdf−1(ξθ)=arccos⁡(1−ξθ(α2−1)ξθ+1)ϕ=2πξϕ

然后即可计算出光线采样点的入射向量ωk→

3.3.2 代码

实际运算时,每个采样点的权重是不一样的,采用和宏法线的点积作为权重

1N∑k=1NLi(ω→k)≈1∑k=1N(n→⋅ω→k)∑k=1NLi(ω→k)(n→⋅ω→k)
// make the simplyfying assumption that V equals R equals the normal 
vec3 R = N;
vec3 V = R;

const uint SAMPLE_COUNT = 1024u;
vec3 prefilteredColor = vec3_splat(0.0);
float totalWeight = 0.0;

for(uint i = 0u; i < SAMPLE_COUNT; ++i)
{
    // generates a sample vector that's biased towards the preferred 
    //alignment direction (importance sampling).
    vec2 Xi = Hammersley(i, SAMPLE_COUNT);
    vec3 H = ImportanceSampleGGX(Xi, N, roughness);
    vec3 L  = normalize(2.0 * dot(V, H) * H - V);

    float NdotL = max(dot(N, L), 0.0);
    if(NdotL > 0.0)
    {
        prefilteredColor += texture2D(s_texSkybox, SampleSphericalMap(L)).rgb * NdotL;
        totalWeight      += NdotL;
    }
}

prefilteredColor = prefilteredColor / totalWeight;

3.3 BRDF部分

观察公式的第二个拆分项

1N∑k=1Nfs(ωk→,ωo→)(ωk→⋅n→)pdf(ωk→)

其中

fs(ω→k,ω→o)=F(ω→k,ω→h)D(ω→h)G(ω→k,ω→o)4(ωk→⋅n→)(ωo→⋅n→)pdf(ωk→)=pdf(ωh→)4(ωh→⋅ωo→)=D(ωh→)(ωh→⋅n→)4(ωh→⋅ωo→)F(ω→k,ω→h)=F0+(1−F0)(1−ωh→⋅ωo→)5

设Fc=(1−ωh→⋅ωo→)5,那么

F(ω→k,ω→h)=F0(1−Fc)+Fc

带入上面的式中,可得

fs(ωk→,ωo→)(ωk→⋅n→)pdf(ωk→)=F0fs(ωk→,ωo→)F(ω→k,ω→h)pdf(ωk→)(1−Fc)(ωk→⋅n→)+fs(ωk→,ωo→)F(ω→k,ω→h)pdf(ωk→)Fc(ωk→⋅n→)=F0(1−Fc)G(ω→k,ω→o)(ωh→⋅ωo→)(ωh→⋅n→)(ωo→⋅n→)+FcG(ω→k,ω→o)(ωh→⋅ωo→)(ωh→⋅n→)(ωo→⋅n→)

设

Gv=G(ω→k,ω→o)(ωh→⋅ωo→)(ωh→⋅n→)(ωo→⋅n→)

那么可得

1N∑k=1Nfs(ωk→,ωo→)(ωk→⋅n→)pdf(ωk→)=F0(1N∑(1−Fc)Gv)+1N∑FcGv

3.4 BRDF查找贴图

上面的公式中,除了F0和运行时的材质相关,其他数值都只取决于所使用的微表面的函数类型,所以可以预先计算在一张二维贴图中

在运行时可以使用查表得方式从这张贴图上获取数据

1N∑k=1Nfs(ωk→,ωo→)(ωk→⋅n→)pdf(ωk→)=F0∗Tex2D(ωo→⋅n→,Roughness).r+Tex2D(ωo→⋅n→,Roughness).g

3.4.1 BRDF贴图的生成

vec2 IntegrateBRDF(float NdotV, float roughness)
{
    vec3 V;
    V.x = sqrt(1.0 - NdotV*NdotV);
    V.y = 0.0;
    V.z = NdotV;

    float A = 0.0;
    float B = 0.0; 

    vec3 N = vec3(0.0, 0.0, 1.0);
    
    const uint SAMPLE_COUNT = 1024u;
    for(uint i = 0u; i < SAMPLE_COUNT; ++i)
    {
        // generates a sample vector that's biased towards the
        // preferred alignment direction (importance sampling).
        vec2 Xi = Hammersley(i, SAMPLE_COUNT);
        vec3 H = ImportanceSampleGGX(Xi, N, roughness);
        vec3 L = normalize(2.0 * dot(V, H) * H - V);

        float NdotL = max(L.z, 0.0);
        float NdotH = max(H.z, 0.0);
        float VdotH = max(dot(V, H), 0.0);

        if(NdotL > 0.0)
        {
            float G = GeometrySmith(N, V, L, roughness);
            float G_Vis = (G * VdotH) / (NdotH * NdotV);
            float Fc = pow(1.0 - VdotH, 5.0);

            A += (1.0 - Fc) * G_Vis;
            B += Fc * G_Vis;
        }
    }
    A /= float(SAMPLE_COUNT);
    B /= float(SAMPLE_COUNT);
    return vec2(A, B);
}

Prev
环境光渲染(一)