(论文)[1997-SIG] Metropolis Light Transport(3)

Metropolis Light Transport

mitsuba0.6 实现

  • 总变异数:spp x resolution
    • 每个样本变异数目:~20w
    • 样本数目 seedCount
  • 估计 luminance(10w 随机采样,求平均值)
    • 同时记录采样得到的路径以及 luminance
    • 在其中根据 luminance 重采样得到 seedCount 数目的初始路径
      • 【重采样让更近似接近于 \(f(\bar{x})\),减轻 startup bias】
  • 每条初始路径独立处理【进入 process() 函数】
  • process()
    • 根据初始种子构建原始路径 current,并计算 relWeight\({\color{red}\dfrac{f}{p}G}\)
    • 进入变异循环
  • 变异循环
    • 检测当前路径所有合适的变异方法,均匀采样一种变异方法(mutator)
    • 根据选择的 mutator 采样新路径
    • 计算 Qxy【论文中的 \(f(\bar{x})T(\bar{y}|\bar{x})\)】、Qyx
    • 根据规则累计平均 luminance
  • develop() 统计直方图
  • 写优化
    • 如果变异没有被接受,那么先不累计【权重需要加】,等到最后被放弃的时候在累积到结果中

变异例子

  • LensPerturbation 为例【感觉整体来说挺复杂的】

  • 前置

    • path 光源开始编号 0,边的长度 \(len\)
      • 如下是一个示例光路类型 \(len=5\)
    1
    2
    3
    4
    5
    6
    emitterSupernode
    emitterSample
    surfaceInteraction
    surfaceInteraction
    sensorSample
    sensorSupernode
    • emitterSupernode 中记录了光源 radiance 信息;类似的 sensorSupernode 记录了相机 importance 信息
    • 屏幕扰动范围:\([r_1,r_2]=[0.1,\sqrt{0.05\times\dfrac{\text{resolution}}{\pi}}]\)
  • suitability():适用条件

    • \(len-1\) 开始【因为有 supernode】,第一个 connectable 的顶点记作 \(k\)
      • 边的数目 \(len\),顶点数目 \(len+1\)
    • \(k>0\) 而且 \(k-1\) 顶点也需要 conectable
  • sampleMutation():变异

    1
    2
    3
    edge    |                  l            m
    | o --- o --- o' --- o' --- o --- o
    vertex | l m k
    • 找到上述顶点 \(k\)【记 \(l=k-1,m=len-1\)
      • ' 表示 connectable
    • 在屏幕空间采样 offset
      • \(r=r_2\exp(-\ln\dfrac{r_2}{r_1}\mathcal{U}_1);\phi=2\pi\mathcal{U}_2\)
      • 如果超出屏幕/相机采样不出来这个点,拒绝采样
    • 构建 proposed path【要求顶点数和 current path 一样,顶点类型一样,顶点的 connectable 一样】
      • 拷贝基础路径:除了点 \([l+1,m-1)\),边 \([l+1,m-1]\) 之外,都拷贝 current path;这几个点先用空值,然后使用下面的方式更新
        • 细节:不变的点用浅拷贝,会变化的点用深拷贝
      • vertex.perturbDirection():扰动相机顶点【\(m\)】(sensorSample),根据上面的采样
        • edge.perturbDirection():更新边,同时计算下一个交点【\(m-1\)
      • vertex.propagatePerturbation():顶点依次传播扰动,\((l+1,m-1]\)
        • 采样下一个方向的时候,使用固定随机数 \([0.5,0.5]\),【是因为反正是 specular 吗?至少是 unconnectable】
        • edge.perturbDirection()
      • 连接顶点 \(l,l+1\)
      • 记录结构体 MutationRecord
        • \(ka = m - l\)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      struct MutationRecord {
      Mutator::EMutationType type; ///< Type of executed mutation
      int l; ///< Left vertex of the affected range
      int m; ///< Right vertex of the affected range
      int ka; ///< Size of the insertion
      Spectrum weight; ///< Spectral weight of the unchanged portion
      int extra[5];
      };

      struct MutationRecord muRec;
  • Q:计算 \(f(\bar{x})T(\bar{y}|\bar{x})\)

    • muRec.weight:记录了 \([0,l),(m,k]\) 的边、\([0,l),[m,k-1)\) 的边的权重累乘
    • 乘上连接的边的权重、扰动的边的权重;\(T\) 的计算包含在这里面了,参考这个
    • 这里的 \(f(\bar{x})\) 指的是在在原始采样框架下的无偏估计
    • 哎哟我的天呐,weight 这一块计算复杂了,感觉看不太懂
  • 累计结果:归一化的 path luminance【Why

特殊

  • twoStage
    • 第一阶段渲染一个低分辨率的图片,上采样之后,作为 \(I(pixel_x)\)
      • \(pixel_x\) 表示 \(x\) 所在的像素位置
    • 第二阶段近似的时候,mlt 的近似目标变为 \(\dfrac{f(\bar{x})}{I(pixel_{\bar{x}})}\)
      • 就是 \(\dfrac{f(\bar{x})}{I(pixel_{\bar{x}})}\) 替换 \(f(\bar{x})\)
    • 效果上而言,让每个像素的计算资源分配更加均匀
  • separateDirect:直接光的渲染不使用 MLT,而是直接算
  • 支持的变异种类
1
2
3
4
5
6
7
8
9
10
enum EMutationType {
EBidirectionalMutation = 0,
ELensPerturbation,
ELensSubpathMutation,
EIndependentMutation,
ECausticPerturbation,
EMultiChainPerturbation,
EManifoldPerturbation,
EMutationTypeCount
};

mitsuba 相关

process 逻辑

  • 构建一个 Process,绑定资源,然后交给 Scheduler
    • scheduler->schedule(process);
  • schedule() 逻辑
    • 将 process 加入到任务队列里面,然后通知所有线程(Worker)
    • Worker 在函数 acquireWork() 中被唤醒【Worker 状态类似于 while(acquireWork()) {}
      • 调用 process->createWorkProcessor() 构建 WorkProcessor
      • 调用 process->generateWork()
      • 调用 WorkProcessor 的 process() 开始工作
      • 工作完调用 releaseWork() 通知 Scheduler,进入休眠
  • 【非统一接口】所有并行程序运行完之后,后处理
    • process->develop();
  • 默认的 integrator 行为就是和上面类似的:参考文件 mitsuba/src/librender/integrator.cpp
    • process() 中逐点调用 Li()

一些疑问

bsdf

  • sensor subpath 对应 Radiance【常规 PT】;emitter subpath 对应 Importance
  • mitsuba 里面 \(w_i\) 指的是生成路径时前一个点指向当前点
    • sensor:相机方向指向当前点
    • emitter:光源方向指向当前点
  • BSDF evaluate 的时候都是使用相同的形式,这个是正确的吗?
    • 就都是 bsdf->eval(wi, wo)【而不需要将 \(w_i\) 都用光源方向吗?】
    • mitsuba/src/libbidir/vertex.cpp::Line35::sampleNext()
    • 不太懂,之后看 BDPT 再看吧