1 前言

这次的光线追踪,主要是基于 Radiometry 的一种实现,也就是基于物理的一种实现。本文对辐射度量学做了解释,同时给出了程序中的关键代码以及参考资料,实现了微表面模型,模型的代码正确无误,但是运用到路径追踪上会出现很强的噪声,以目前的实力无法解决。

2 辐射度量学

关于辐射度量学,这里给出一种理解方式,

  • Radiant energy (barely used in CG) \(Q [J = Joule]\) :The energy of electromagnetic radiation

Raiant energy说白了就是能量。可以类比于位置,假设初始的位置是 0 m, 最终的位置是 100 m。我们可以说,兔子一开始在0,最后在100, 我们也可以说乌龟一开始在0,最终在 100。很显然,位置没有什么用,我们必须要把位置和时间结合起来,才可以获得更加直观和直觉的信息。

我们不会说自行车和汽车都能走100公里,而是会说走这100公里的速度。

一个10w的灯泡可以放出1000J的能量,可这个时间就可能是开10个小时。一个100w的灯泡也可以放出1000J的能量,这个时间可能是1个小时。我们直觉的感到,10w的灯泡使用的时候会比100w的暗淡,如何描述这种性质呢?就引出了

  • Radiant flux(power) $ \Phi \equiv \frac {dQ}{dt} $ [W=Watt][lm=lumen] : Energy per unit time

我们可以采用量子化的方法,把一份能量看作一个小球。而 Radiant flux(power) 就是描述 一秒内 能射出出多少个小球。

这可以使用向量来表示,小球的射出方向就是向量的方向,小球的大小就是向量的大小。需要注意的一点是,在物理中,向量只有大小和方向,没有所谓的固定的起始点。

Radiant flux(power)就是包蓝色的小球。一包 power 里有许多 能量(黄色的小球),我们用蓝色的时间包裹,就成了power。这是辐射度量学里常用的东西,也是基本的东西。而 power 比 energy更好描述这个世界,我们可以将它比喻为力量感或者什么东西,它代表了某种强度。

  • Radiant intensity $I(\omega) \equiv \frac {d\Phi }{d\omega } $ :power per unit solid angle。

  • Solid Angle $ \Omega $ = $ \frac {A}{r^ {2}} $ :ratio of subtended area on sphere to radius squared

一个物体某个点发出的黄色的小球会朝着空间中四散而去, 在一个锥形的固体角中,一秒中的黄色小球数量。

需要注意的是,上述公式中用到了\(d\), 我们知道这是一个极限的过程,立体角的定义是球面上某处面积与整个球面面积之比,当处于极限过程时,一块面积趋近于一个非常非常小的点,而这时立体角就可以看作是一个箭头。有了这个思想,就接下来就好做多了。

光线可以理解为一个糖葫芦。穿着power。

  • Irradiance

    Definition: The irradiance is the power per unit area incident on a surface point.
\[E(x)= \frac {d\Phi(x)}{dA}
\]
\[[\frac {W}{m^ {2}}] [ \frac {lm}{m^ {2}} =lux]
\]

可以理解为,四面八方来的光线这个面积上,可以引申为这个点上,由于极限的关系。

  • Radiance

    Definition: The radiance is the power emitted, reflected,transmitted or received by a surface, per unit solid angle, per projected unit area.
\[L(p, \omega)= \frac {d^ {2}\Phi (p,\omega )}{d\omega dA\cos \theta }
\]

Radiance是光线Ray传播时(即不与物体交互时)的一个性质。他在物体发出光线,或者光线打到物体上时会有折损,这种折损与物体表面的法线方向与传播光线的方向有关** Lambert’s Cosine Law**。所以我们用Radiance表示光线的传播。

一个光线带有100power,打到物体上由于角度关系我们只得到了50power, 但是我们不能说光线是50power的。

\[L(p, \omega )= \frac {dE(p)}{d\omega \cos \theta }
\]
\[L(p, \omega ) \cos \theta d\omega = dE(p)
\]

接收到的 Irradiance 由于在接收时,会因为 Lambert’s Cosine Law而进行缩小, 所以在还原时,需要除以 \(\cos \theta\)进行放大,还原。

3 Coding

Pseudo Code

shade(p, wo)
Uniformly sample the light at xx (pdf_light = 1 / A)
Shoot a ray from p to x
If the ray is not blocked in the middle
L_dir = L_i * f_r * cos_theta * cos_theta_x / |x-p|^2 / pdf_light
L_indir = 0.0
Test Russian Roulette with probability P_RR
Uniformly sample the hemisphere toward wi (pdf_hemi = 1 / 2pi)
Trace a ray r(p, wi)
If ray r hit a non -emitting object at q
L_indir = shade(q, wi) * f_r * cos_theta / pdf_hemi / P_RR
Return L_dir + L_indir

3.1 uniform random point in triangle in 3D

	// Triangle::Sample()
void Sample(Intersection &pos, float &pdf){
float x = std::sqrt(get_random_float()), y = get_random_float();
pos.coords = v0 * (1.0f - x) + v1 * (x * (1.0f - y)) + v2 * (x * y);
pos.normal = this->normal;
pdf = 1.0f / area;
}

在Triangle::Sample()里给出了三角形内随机采样某一点的算法,来历如下。

https://www.cs.princeton.edu/~funk/tog02.pdf

https://math.stackexchange.com/questions/18686/uniform-random-point-in-triangle-in-3d

3.2 Multithreading

 	int spp = 64;

    std::vector<std::thread> threads;
std::vector<Vector3f> colors(spp); /* Muti-threads implenmetation for each Ray
Ray ray(eye_pos, dir);
for(int i = 0; i < spp; i++){
threads.emplace_back( std::thread(&Renderer::RT_thread, this, std::ref(colors[i]), std::ref(scene), ray) );
}
for(int i = 0; i < threads.size(); i++){
threads[i].join();
}
for (int k = 0; k < spp; k++){
framebuffer[m] += colors[k] / spp;
}
threads.clear();
*/

https://www.educative.io/blog/modern-multithreading-and-concurrency-in-cpp

3.3 Eval()

使用了 Diffuse / Lambertian Material 的BRDF Lecture17 P8。总计两条假设

  1. 入射的radiance为常数。Suppose the incident lighting is uniform

  2. 各个方向的出射radiance为常数 Light is equally reflected in each output direction

关于 ρ=1 时,就是所谓的入射与反射的能量相同。设一条入射光带着 x 能量打到表面,这些能量会均匀的向四面散开,这时某一方向的出射光能量很少,而有无数条从任意方向来的入射光带着 x 能量打到表面,某一方向的出射光 能量就等于 x。这建立在上述两条假设成立的情况下。

Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
switch(m_type){
case DIFFUSE:
{
// calculate the contribution of diffuse model
float cosalpha = dotProduct(N, wo);
if (cosalpha > 0.0f) {
/*
Vector3f diffuse = Kd / M_PI;
return diffuse;
*/
// wi is the cam_to_pos, wo is pos_to_light, so we need to reverse the wi let them all outward for the
// compute of the cos
return brdfMicrofacet(-wi, wo, N, 0.5f, 0.2, Kd, 0.5);
}
else
return Vector3f(0.0f);
break;
}
}
}

3.4 Sample()

https://www.zhihu.com/question/522815454

对于水平和垂直方向的极坐标角度θ和φ分别采样(利用三角函数将其控制在合适的区间内),然后再转化为直角坐标。

由于这个半球面是以 Normal 为中心的,所以需要进行一个变换,代码中为 toWorld()。由于代码中并没有给出这个变换的来源与依据, 经过测试, wi是一个单位向量。比较好的方法是,既然已经得到了一个球面的采样,与一个normal,要以这个 normal 定义的半球面采样,只需要判断这个球面的向量与normal的夹角,如果为正那很好,若为负数,直接取逆即可。

Vector3f Material::sample(const Vector3f &wi, const Vector3f &N){
switch(m_type){
case DIFFUSE:
{
// uniform sample on the hemisphere
float x_1 = get_random_float(), x_2 = get_random_float();
float z = std::fabs(1.0f - 2.0f * x_1);
float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;
Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);
return toWorld(localRay, N); break;
}
}
}

4 Microfacet Model

GGX分布函数,可以理解为这个表面和 h 一样方向的有多少 [0-1]

G_Smith阴影遮蔽函数主要是由两部分的乘积组成,一个是 cam2pos 和 light2pos 这两个都会产生遮挡。

我们常会遇到这两个GGX分布函数的表达式,实际上他们两个是一样的, \(\theta_m\) 为 half-vector 与 normal 的夹角。

\[D_{GGX}(\mathbf{m}) = \frac{\alpha^2}{\pi((\mathbf{n} \cdot \mathbf{m})^2 (\alpha^2 - 1) + 1)^2} \\
\]
\[D_{GGX}(\mathbf{m}) = \frac{\alpha^2}{\pi\cos^4\theta_m (\alpha^2 +\tan ^2\theta_m)^2} \\
\]
\[\mathbf{n} \cdot \mathbf{m} = \cos \theta_m
\]
\[\tan \theta_m = \frac{\sin \theta_m}{ \cos \theta_m}
\]
    // For Microfacet Model 2007 Walter
// Author:GSN Composer from youtube
// lihgt = in, view = out, N = Macro Normal
// metallic use to define the dielectric(glass air and plastic with 0.0f) metal(Iron copper and gold with 1.0f)
// roughness using at GGX-D and G_Smith function
// relectance use to modifine the F0(Fresnel relectance), eg. if2 F0 = 0.04 the material is Glass
// baseColor use to the BRDF diffuse part and also F0(Fresnel relectance) whth metal
inline Vector3f brdfMicrofacet(Vector3f Light, Vector3f View, Vector3f N, float metallic, float roughness, Vector3f baseColor, float reflectance); inline Vector3f fresnelSchlick(float cos, Vector3f F0); inline float D_GGX(float NOH, float roughness); inline float G_Smith(float NoV, float NoL, float roughness); inline float G1_GGX_Schlick(float NoV, float roughness);
Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
switch(m_type){
case DIFFUSE:
{
// calculate the contribution of diffuse model
float cosalpha = dotProduct(N, wo);
if (cosalpha > 0.0f) {
/*
Vector3f diffuse = Kd / M_PI;
return diffuse;
*/
// wi is the cam_to_pos, wo is pos_to_light, so we need to reverse the wi let them all outward for the
// compute of the cos
return brdfMicrofacet(-wi, wo, N, 0.5f, 0.2, Kd, 0.5);
}
else
return Vector3f(0.0f);
break;
}
}
} Vector3f Material::brdfMicrofacet(Vector3f Light, Vector3f View, Vector3f N, float metallic, float roughness, Vector3f baseColor, float reflectance)
{
// half vector
Vector3f H = normalize(Light + View); float NoL = clamp(0.001f, 1.f, dotProduct(N, Light));
float NoV = clamp(0.001f, 1.f, dotProduct(N, View));
float NoH = clamp(0.001f, 1.f, dotProduct(N, H));
float VoH = clamp(0.001f, 1.f, dotProduct(View, H)); Vector3f F0(0.16 * (reflectance * reflectance)); F0 = lerp(F0, baseColor, metallic); Vector3f F = fresnelSchlick(VoH, F0); float D = D_GGX(NoH, roughness);
float G = G_Smith(NoV, NoL, roughness); Vector3f spec = (F * D * G) / (4.0f * NoV * NoL); Vector3f rolD = baseColor; // only the transmitted part contribute to diffuse for the energy conservation;
rolD = rolD *(Vector3f(1.0f) - F); // the diffuse part for the metal matriels must be equal to 0.0f;
rolD = rolD * (1.0f - metallic); Vector3f diff = rolD / M_PI; return diff + spec;
} inline Vector3f Material::fresnelSchlick(float cos, Vector3f F0)
{
return F0 + ( Vector3f(1.0f) - F0 ) * std::pow(1.0 - cos, 5.0);
} inline float Material::D_GGX(float NoH, float roughness)
{
float alpha = roughness * roughness;
float alpha2 = alpha * alpha;
float NoH2 = NoH *NoH;
float b = NoH2 * (alpha2 -1.f) + 1.0f;
return alpha2 / (M_PI * b * b);
} inline float Material::G_Smith(float NoV, float NoL, float roughness)
{
return G1_GGX_Schlick(NoV, roughness) * G1_GGX_Schlick(NoL, roughness);
} inline float Material::G1_GGX_Schlick(float NoV, float roughness)
{
float alpha = roughness * roughness;
float k = alpha * 0.5f;
return NoV /(NoV * (1.0f - k ) + k);
}

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

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

https://mitsuba2.readthedocs.io/en/latest/generated/plugins.html#rough-conductor-material-roughconductor

Microfacet BRDF: Theory and Implementation of Basic PBR Materials [Shaders Monthly #9]: https://www.youtube.com/watch?v=gya7x9H3mV0

GGX 2007 :https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf

Games101 基于蒙特卡洛积分的光线路径追踪 作业7 框架解读的更多相关文章

  1. 全局光照:光线追踪、路径追踪与GI技术进化编年史

    全局光照(Global Illumination,简称 GI), 作为图形学中比较酷的概念之一,是指既考虑场景中来自光源的直接光照,又考虑经过场景中其他物体反射后的间接光照的一种渲染技术. 大家常听到 ...

  2. Knowledge Tracing -- 基于贝叶斯的学生知识点追踪(BKT)

    目前,教育领域通过引入人工智能的技术,使得在线的教学系统成为了智能教学系统(ITS),ITS不同与以往的MOOC形式的课程.ITS能够个性化的为学生制定有效的 学习路径,通过根据学生的答题情况追踪学生 ...

  3. [python] A*算法基于栅格地图的全局路径规划

    # 所有节点的g值并没有初始化为无穷大 # 当两个子节点的f值一样时,程序选择最先搜索到的一个作为父节点加入closed # 对相同数值的不同对待,导致不同版本的A*算法找到等长的不同路径 # 最后c ...

  4. 基于全注解的SpringMVC+Spring4.2+hibernate4.3框架搭建

    概述 从0到1教你搭建spring+springMVC+hibernate整合框架,基于注解. 本教程框架为基于全注解的SpringMVC+Spring4.2+hibernate4.3,开发工具为my ...

  5. 基于netty轻量的高性能分布式RPC服务框架forest<下篇>

    基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开 ...

  6. 基于netty轻量的高性能分布式RPC服务框架forest<上篇>

    工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...

  7. 基于MVC4+EasyUI的Web开发框架形成之旅--框架总体界面介绍

    在前面介绍了一些关于最新基于MVC4+EasyUI的Web开发框架文章,虽然Web开发框架的相关技术文章会随着技术的探讨一直写下去,不过这个系列的文章,到这里做一个总结,展示一下整体基于MVC4+Ea ...

  8. 接口自动化 基于python实现的http+json协议接口自动化测试框架源码(实用改进版)

    基于python实现的http+json协议接口自动化测试框架(实用改进版)   by:授客 QQ:1033553122 欢迎加入软件性能测试交流QQ群:7156436     目录 1.      ...

  9. GPUImage ==> 一个基于GPU图像和视频处理的开源iOS框架

    Logo 项目介绍: GPUImage是Brad Larson在github托管的开源项目. GPUImage是一个基于GPU图像和视频处理的开源iOS框架,提供各种各样的图像处理滤镜,并且支持照相机 ...

  10. 基于mui的H5套壳APP开发web框架分享

    前言 创建一个main主页面,只有主页面有头部.尾部,中间内容嵌入iframe内容子页面,如果在当前页面进行跳转操作,也是在iframe中进行跳转,而如果点击尾部按钮切换模块.页面,那就切换ifram ...

随机推荐

  1. pip install --user 使用方法和注意事项——python中安装module库到用户packages路径中

    pip install --user   是python中安装module库到用户packages路径中的方法. 参考: https://blog.csdn.net/The_Time_Runner/a ...

  2. 从零体检一个魔塔社区模型(modelscope)最简单demo

    从社区拿一个模型,比如以下这个链接 https://www.modelscope.cn/models/iic/cv_mobilenet-v2_bad-image-detecting 它的代码样例如下 ...

  3. sublime添加GBK编码格式

    1.背景 2.步骤 Tools(工具) --->  Install Package Control...(安装控制包) 点击执行完成后继续下一步: 点击Package Control,随后搜索I ...

  4. MFC对话框程序:实现程序启动画面

    MFC对话框程序:实现程序启动画面 对于比较大的程序,在启动的时候都会显示一个画面,以告诉用户程序正在加载,或者显示一些关于软件的信息,如Visual C++,Word, PhotoShop等.那么对 ...

  5. [最新] Chrome 添加 Cookie 标红的解决方法(测试于119)

    最近发现 Chrome 开发人员工具里无法添加 Cookie,输入名称时整行变红,而且不会自动补充域.大小.过期时间等项. 网上搜全都是 Chrome 96 要开 Partitioned Cookie ...

  6. Plateau-Rayleigh 不稳定性 + Young-Laplace 方程

    考虑竖直下落水柱中的 \(AB\) 两点 \[\begin{matrix} \displaystyle\frac12\rho U_0^2+\rho gz+P_A=\frac12\rho U^2(z)+ ...

  7. 自签名证书实现浏览器IP证书访问

    创建文件夹 mkdir ssl cd ssl #IP.2 改成自己电脑的IP地址或服务器IP vi ext.ext keyUsage = nonRepudiation, digitalSignatur ...

  8. 【Azure Logic App】在逻辑应用中开启或关闭一个工作流是否会对其它工作流产生影响呢?

    问题描述 使用标准版的Azure Logic App服务,可以创建多个工作流(workflow),如果在启用/禁用其它的工作流时,是否会对正在运行其它工作流造成影响呢? 问题解答 在实际的测验中,我们 ...

  9. Navicat16 安装破解教程

    Navicat16 安装破解教程 Navicat 16.1 什么是Navicat? 官网下载Navicat 注册包的使用 Navicat 16.1 在文章最后添加工作号 回复关键词获取注册机 什么是N ...

  10. 【YashanDB知识库】v$instance视图中实例角色含义不明确

    [标题]v$instance视图中实例角色含义不明确 [问题分类]文档问题 [关键词]YashanDB, v$instance, 实例角色 [问题描述]v$instance视图中实例角色(如MASTE ...