最近,我问了一个有关如何在路径跟踪器中混合光泽和漫反射着色器的问题:混合着色器在路径跟踪器上看起来不对。
我认为这是不正确的,但是将我的内容与Blender的内容进行比较似乎是正确的。但是,现在我迷失了如何使镜子的表面看起来粗糙,如本例所示:



我做了一些研究,发现有不同类型的反射模型,例如Beckmann和GGX。我已经找到了一些有关如何找到BRDF的解释,例如以下站点:Specular BRDF,但是我找不到有关如何在显式照明下反射光线的任何解释。如我的路径跟踪器的伪代码所示(如下所示),对于每个对象,我都会向光线发射光线,并使用表面传输渲染方程式合并BRDF。我假设这是GGX / Beckmann BRDF插入的地方。(我猜想它并不是那么简单,并且一定要涉及到概率)。真正让我着迷的是那条反射射线。对于漫射而言,这很容易,因为我只是在表面法线半球的任意位置发出随机射线。但是,对于镜面反射而言,BRDF的冲击更大。我如何将其转换为反射射线?如果我只是使理想的反射光线抖动一点,那与在GGX /贝克曼反射中如何对微面建模是没有关系的。 >
$$ \ large L_S(\ mathbf x,\ mathbf k_o)\ approx \ frac {\ rho(\ mathbf k_i,\ mathbf k_o)L_e(\ mathbf x',\ mathbf x- \ mathbf x' )v(\ mathbf x,\ mathbf x')\ cos \ theta_i \ cos \ theta'} {p(\ mathbf x')\ left \ lVert \ mathbf x- \ mathbf x'\ right \ rVert ^ 2} $ $

其中$ p(\ mathbf x)$是浅三角形$ 1 / \ text {Area} $

$ \ mathbf x'$是一个浅三角形上的随机点

$ \ mathbf x $是对象的命中点

余弦是光线的法线和光线之间的夹角,而物体的法线和光线之间的夹角

$ \ rho $是BRDF($ 1 / \ pi $用于理想的漫反射)

$ v $是$ 1 $还是$ 0 $,具体取决于它是否在阴影中
伪代码:

rayColor(ray r, depth, int E=1)
{
    if(r doesn't hit triangle)
        return 0
    if(r hit is a light)
    {
       if(E)
         return light_emission
       else
         return 0
    }

    vector x = r.origin + r.direction*t // x is point where r hit tri
    vector n = normal where ray hit triangle
    n.normalize()
    vector nl = n.dot(r.d) < 0 ? n : n*(-1) // properly orient normal

    if(++depth > 5) return 0 // max bounces

    float triangle_area   = area of emitting triangle
    vector x_light_random = random point on emitting triangle
    vector light_normal   = normal of emitting triangle

    vector d = x_light_random = x_converted;


    if(light_normal.dot(d) > 0) light_normal *= -1; // make it emit
                                                    // both sides

    object_normal.normalize();
    light_normal.normalize();

    BRDF = 1/PI // perfect diffuse
    light_emitted = 1 // emission of 1
    vector light_out = 0
    if(ray starting at x towards d hits light (i.e. not in shadow))
    {
         light_out = BRDF*light_emitted*(object_normal.dot(d))*
                            (-1*light_normal.dot(d)*triangle_area)/
                            (d.length*d.length*d.length*d.length)
    }
    vector direct_light = color_of_object_triangle*light_out;



  //----SPECULAR-----
  vector d2 = r.dir-n*2*n.dot(r.dir); // ideal reflection
  vector light_color = 1 // white since dialectics don't change spec
  vector specular = light_color*rayColor(createRay(x, d2),depth)

  float P = 0.5; // 50/50 chance of mirror/diffuse
  if(erand(Xi) < P)
      return direct_light/P
  else
      return spec/(1-P)


#1 楼

对于显式光采样:是的,您只需评估BRDF的入射方向和输出方向,使其返回相机。面光源的情况涉及概率:您必须随机选择光源上的一个点,并包括一个将光源区域上的概率密度转换为接收点上的立体角密度的因子。看来您已经知道了(这是$ \ cos \ theta'/(p(\ mathbf {x}')\ | \ mathbf {x}-\ mathbf {x}'\ | ^ 2)$您提到的公式)。

要采样反射射线,您只需从半球中拾取随机射线并由BRDF对其进行加权即可,这与漫反射相同。但这不是很有效。要获得低噪声的结果,将需要大量样本。

您真正想要做的是对BRDF进行重要性采样。这意味着在半球上并非均匀地选择随机射线,而是使用与BRDF的形状匹配的概率分布(尽可能)。然后,当粗糙度较低时,您将自动获得聚集在BRDF中尖锐反射峰周围的光线,而当粗糙度较高时,光线会在半球中更多地散布。

那么你怎么做它? Walter等人的论文《通过粗糙表面折射的微面模型》(普及了图形中的GGX分布)在5.2节中方便地给出了几种常见BRDF的重要性采样公式。他们的方法是对BRDF的正态分布进行重要性采样,然后将菲涅耳和几何因子留在路径权重中。

例如,他们的GGX重要性采样公式为(等式35 –36):
$$ \ begin {aligned} \ theta_m&= \ arctan \ left(\ frac {\ alpha_g \ sqrt {\ xi_1}} {\ sqrt {1- \ xi_1}} \ right)\ \ \ phi_m&= 2 \ pi \ xi_2 \ end {aligned} $$
其中$ \ alpha_g $是GGX粗糙度参数,而$ \ xi_1,\ xi_2 $是[0,1]中的一对统一随机数。输出$ \ theta_m,\ phi_m $是采样的微面法线的极坐标(相对于表面法线)。

因此,要使用此选项,您可以选择随机输入$ \ xi_1,\ xi_2 $,然后对以上方程式求值,并使用所得法线计算反射射线以及BRDF的菲涅耳和几何因子。

评论


$ \ begingroup $
非常感谢!现在,一切对我来说都变得更加有意义。那篇论文也是很棒的资源,因为我的下一个步骤将是在完成镜像后实施玻璃。
$ \ endgroup $
–亨利·贝尔金(Henry Bergin)
17年1月8日在21:23