15

关于粗粒NDF的尝试

 3 years ago
source link: https://zhuanlan.zhihu.com/p/99192236
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

关于粗粒NDF的尝试

想做好看的东西

2020.2.19:

点光源效果,镜头移动周围亮点闪烁:

v2-41b4ff75a46076640ebff9fe9a3bd5b0_720w.jpg

题图 细粒度的NDF vs 粗粒度的NDF(图片来自Naty Hoffman, Recent Advances in Physically Based Shading, SIGGRAPH 2016)

现今使用的NDF(D)从外观而言都很平滑,如题图中左边的NDF。这种NDF每个像素覆盖了数万个表面细节,是对细粒度的微观几何的一种良好表示形式。

但其实真实世界中的许多表面材质,具有粗粒度的微观结构,像素仅覆盖了几十个表面元素。 在这种情况下,法线分布的表现更像是如题图的右边所示,表面有一个复杂而闪烁外观,而不仅仅的各项异性这么简单。目前提出的模型都无法表示出这种类型的法线分布。

(在@毛星云前辈的文章里看到的)

https://zhuanlan.zhihu.com/p/69380665

我在这里试着给出一个生成粗粒法线分布的方法:用一个函数把细粒NDF转换成粗粒NDF。

分享一下我的思路╭(′▽`)╯

设每一个 理想细粒片元 中有 N_ideal 个(数万个以上)理想微小镜面。(N_ideal的具体数值没有意义,在计算中不会用到 N_ideal。)

设 每一个 “真实粗粒片元” 中有 N_real 个(数十个)理想微小镜面。

把一个 “理想细粒片元” 分割成 N_ideal/N_real 份后,取其中一份放大,就得到了一个 “真实粗粒片元”。

设 理想细粒模型 中 采用的“理想法线分布函数”为 D_ideal (比如GGX)。一个“理想细粒片元” 中 “微法线m=H”的概率为 此理想片元 D_ideal 的值。

所以对于粗粒片元中的 每一个微法线m,(“m=H”的概率)= D_ideal。

“m=H” 可以看作一个 伯努利实验,每一个 粗粒片元 中有N_real个“m=H”伯努利实验。所以可以构造一个二项分布:

(k为“此粗粒片元中m=H”的数量。)

然后用inverse CDF,输入一个0~1的完全随机数,输出服从二项分布的“粗粒片元中m=H的数量”;再除以N_real,得到“粗粒片元中m=H的概率”(也就是此片元粗粒NDF的值):

( i 为0~1的随机数,用噪声图采样实现。)

噪声图生成方法:

#include <iostream>
#include <fstream>
using namespace std;

int main(int argc, const char * argv[]) {

    ofstream ouTT;   //流名称

    ouTT.open("/file/noise1000.ppm");  //流写入位置
    int nx = 1000;
    int ny = 1000;

//    ouTT.open("/file/noise5000.ppm");
//    int nx = 5000;
//    int ny = 5000;

    ouTT << "P3\n" << nx << " " << ny << "\n255\n";

    for (int j = ny-1; j >= 0; j--) {
        for (int i = 0; i < nx; i++) {
            float ran = random()%256;
            ouTT << ran << " " << ran << " " << ran << "\n";
        }
    }

    ouTT.close();
}

glsl函数代码:

// ----------------------------------------------------------------------------
//粗粒分布转换
float P(float k, float GGX)
{
    //    float cc = C(50,k);

    //===========
    //排列组合
    float n = 10.0;
    float m = k;
    //分子
    float son = 1.0;
    //分母
    float mother = 1.0;
    // 应用组合数的互补率简化计算量
    m = m > (n - m) ? (n - m) : m;

    if(m > 0)
    {
        for(float i = m;i > 0.0; i--) {
            son *= n;
            mother *= i;
            n--;
        }
    }


    float C = son / mother;
    //===========

    
    
    float p = C * pow((1.0 - GGX), 10.0-k) * pow(GGX, k);

    
    return p;
}
float NDF_Transform(float GGX)
{
    if (GGX > 1.0)
        GGX = 1.0;
    
    float CDF = 0.0;
    float inverseCDF = 10.0;
    
    float Xi = texture(noiseMap, TexCoords).r;
    
    for (float i = 0.0; i < 10.0; i++) {
        CDF += P(i, GGX);
        
        if (Xi <= CDF) {
            inverseCDF = i;
            break;
        }
    }
    
    float D_real = inverseCDF / 10.0; //一个粗粒片元里m=H的概率
    
    return D_real*100;
}
// ----------------------------------------------------------------------------

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK