本文主要参照 Ray Tracing: The Rest of Your Life,其中只是主要精炼光追相关理论,具体实现可参照原文。

前面文章中我们也提到要减少噪点,就是图片上的黑点点。简单说一下为什么会有那么多小点点,就是因为光线路径中没有触碰到光源,路径计算之后就会是黑色的点,我们可以通过发射大量的光线,比如计算每个像素点的时候发射8k~1w条采样光线进行路径计算;也可以路径计算方面做文章:比如加深路径计算递归深度;等等诸如此类。但是上述方法都是暴力解决法,相当耗时,我们可以运用数学对其进行优化,从而实现画质和效率的双面提升,即直接对光源进行采样!

一、理论

我们朝光源方向发送光线或者生成朝向光源的随机方向都是很容易实现的,但是我们需要知道的是,pdf(direction)是什么,如下图对光源采样。

对于一个光源区域A,如果我们均匀采样该区域,那么这个pdf就等于1/A(意思就是每个点的概率均等)。

因此,我们希望尽可能地往重要的方向采样,但在各方向均匀采样,并不会使得重要方向的采样数量变多。接下来,我们人为地让更多的射线往光照的方向采样。

我们可以向着光源的位置生成一个随机的方向,只需在光源材质上随机选取一个点,然后向着这个点的位置生成射线。但我们还需要知道这个pdf(direction)。假设光源的面积为A,如果对这个光源表面随机采样,则该光源表面的pdf为1/A,但如果要从一个定义了方向的单位球的球心为起点,发射射线到这个光源表面,则需要求解对应的pdf公式。

如上图所示,dA 表示光源表面面积的一小部分,设采样到这一小部分面积的概率为 p_q(q) * dA(采样比例乘以微分区域),也就是 dA/A

对于单位球而言,dw 为对应 dA 的单位球表面面积的一小部分,设采样到这一小部分的概率 p(direction) ⋅dw

dwdA 有如下对应关系:

即:方位角微分区域:光源微分区域分成(球心到A中心距离平方)份,取其中的 cosα 代表的份额数。

因为采样到 dwdA 的概率必须相等,则有:

故,

二、代码实现

上述理论整合为代码,如下:

float lightStartX = 213;
float lightEndX = 343;
float lightStartZ = 227;
float lightEndZ = 332;
float lightY = 554;hittable *random_scene() {...list[i++] = new xz_rect(lightStartX, lightEndX, lightStartZ, lightEndZ, lightY, light);...
}vec3 color(const ray& r, hittable *world, int depth) {hit_record rec;if (world->hit(r, 0.001, MAXFLOAT, rec)) {ray scattered;vec3 attenuation;vec3 emitted = rec.mat_ptr->emitted(rec.u, rec.v, rec.p);float pdf;vec3 albedo;if (depth < 50 && rec.mat_ptr->scatter(r, rec, albedo, scattered, pdf)) {//new startfloat sizeX = lightEndX - lightStartX;float sizeZ = lightEndZ - lightStartZ;float light_area = (lightEndX-lightStartX)*(lightEndZ-lightStartZ);vec3 on_light = vec3(lightStartX + random_double()*sizeX, lightY, lightStartZ + random_double()*sizeZ);vec3 to_light = on_light - rec.p;float distance_squared = to_light.squared_length();to_light.make_unit_vector();if (dot(to_light, rec.normal) < 0) return emitted;float light_cosine = fabs(to_light.y());if (light_cosine < 0.000001) return emitted;pdf = distance_squared / (light_cosine * light_area);scattered = ray(rec.p, to_light, r.time());//new endreturn emitted + albedo*rec.mat_ptr->scattering_pdf(r, rec, scattered)*color(scattered, world, depth+1) / pdf;}elsereturn emitted;}elsereturn vec3(0,0,0);
}

渲染结果如下:

上图为每像素采样5次,运行时间与噪点大幅减少,这个效果相当惊艳。这应该是渲染Cornell Box以来最大的突破了,利用重要性采样,将所有Lambertian材质的散射光线指向了光源的方向,减少了大量噪点,且当散射光线直射光源的时候,不再发生散射,大幅减少了射线转折的次数。

不过,天花板下面的的光源周围出现了一些噪点,因为光源是一个两面矩形,距离天花板还有一定的空间。我们可以将其修改为只向下发光:

修改material的虚函数:

class material {public:virtual bool scatter(const ray& r_in,const hit_record& rec, vec3& albedo, ray& scattered, float& pdf) const {return false;}virtual float scattering_pdf(const ray& r_in, const hit_record& rec,const ray& scattered) const {return 0;}/*virtual vec3 emitted(float u, float v, const vec3& p) const {return vec3(0, 0, 0);}*/virtual vec3 emitted(const ray& r_in, const hit_record& rec, float u, float v, const vec3& p) const {return vec3(0, 0, 0);}
};

修改color函数:

vec3 color(const ray& r, hittable *world, int depth) {hit_record rec;if (world->hit(r, 0.001, FLT_MAX, rec)) {ray scattered;vec3 attenuation;//vec3 emitted = rec.mat_ptr->emitted(rec.u, rec.v, rec.p);vec3 emitted = rec.mat_ptr->emitted(r, rec, rec.u, rec.v, rec.p);float pdf;vec3 albedo;if (depth < 50 && rec.mat_ptr->scatter(r, rec, albedo, scattered, pdf)) {//new startfloat sizeX = lightEndX - lightStartX;float sizeZ = lightEndZ - lightStartZ;float light_area = (lightEndX - lightStartX)*(lightEndZ - lightStartZ);vec3 on_light = vec3(lightStartX + random_double()*sizeX,lightY,lightStartZ + random_double()*sizeZ);vec3 to_light = on_light - rec.p;float distance_squared = to_light.squared_length();to_light.make_unit_vector();if (dot(to_light, rec.normal) < 0) return emitted;float light_cosine = fabs(to_light.y());if (light_cosine < 0.000001) return emitted;pdf = distance_squared / (light_cosine * light_area);scattered = ray(rec.p, to_light, r.time());//new endreturn emitted + albedo * rec.mat_ptr->scattering_pdf(r, rec, scattered)*color(scattered, world, depth + 1) / pdf;}elsereturn emitted;}elsereturn vec3(0, 0, 0);
}

修改diffuse_light:

class diffuse_light : public material {public:texture *emit;diffuse_light(texture *a) : emit(a) {}virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const {return false;}virtual vec3 emitted(const ray& r_in, const hit_record& rec, float u, float v, const vec3& p) const {if (dot(rec.normal, r_in.direction()) >= 0.0)return emit->value(u, v, p);elsereturn vec3(0, 0, 0);}/*virtual vec3 emitted(float u, float v, const vec3& p) const {return emit->value(u, v, p);}*/
};

效果如下(采样数50,光源周围的噪点明显减少了):

【光线追踪系列十七】直接光源采样相关推荐

  1. 【光线追踪系列十一】纹理贴图

    本文主要参照 Ray Tracing: The Next Week,其中只是主要精炼光追相关理论,具体实现可参照原文. 一.纹理实现 实现之前,你应该已经充分理解了[光线追踪系列六]反射与金属类特性. ...

  2. 计算机图形学【GAMES-101】9、蒙特卡洛路径追踪(Path Tracing)(光源采样)

    快速跳转: 1.矩阵变换原理Transform(旋转.位移.缩放.正交投影.透视投影) 2.光栅化(反走样.傅里叶变换.卷积) 3.着色计算(深度缓存.着色模型.着色频率) 4.纹理映射(重心坐标插值 ...

  3. Web 前端开发精华文章集锦(jQuery、HTML5、CSS3)【系列十七】

    <Web 前端开发精华文章推荐>2013年第五期(总第十七期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HTML5 和 C ...

  4. 自然语言处理系列十七》中文分词》分词工具实战》Python的Jieba分词

    注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书<分布式机器学习实战>(人工智能科学与技术丛书)[陈敬雷编著][清华大学出版社] 文章目录 自然语言处理系列十七 分词工 ...

  5. Reflex WMS入门系列十七:修改IPG的Grade

    Reflex WMS入门系列十七:修改IPG的Grade Reflex WMS系统里有2个与库存有关的概念,一个是HD,一个是IPG.HD可以简单的理解为托盘,IPG可以理解为放在托盘的物料的库存. ...

  6. 【光线追踪系列十四】蒙特卡洛积分与重要性采样

    本文主要参照 Ray Tracing: The Rest of Your Life,其中只是主要精炼光追相关理论,具体实现可参照原文. 什么是蒙特卡洛积分?简而言之就是,在求积分时,如果找不到被积函数 ...

  7. 【光线追踪系列十】光追加速结构(BVH树)

    本文主要参照 Ray Tracing: The Next Week,其中只是主要精炼光追相关理论,具体实现可参照原文. 经过之前几部分的光追实现,我们大致可以实现了光追的效果,但其中有一个致命的bug ...

  8. Oracle Golden Gate 系列十七 -- GG 一对多 real-time data distribution 说明 与 示例

    一.官网说明 A datadistribution configuration is a one-to-many configuration. Oracle GoldenGatesupports sy ...

  9. 【Python学习系列十七】基于scikit-learn库逻辑回归训练模型(delta比赛代码2)

    机器学习任务流程:学习任务定义->数学建模->训练样本采样->特征分析和抽取->算法设计和代码->模型训练和优化(性能评估和度量)->泛化能力评估(重采样和重建模) ...

最新文章

  1. PyTorch: 序列到序列模型(Seq2Seq)实现机器翻译实战
  2. 推荐65个以自然风光为背景的UI设计
  3. 为什么 P8 程序员的代码你写不出来?零拷贝了解一下
  4. telecom js
  5. Java中Date各种相关用法
  6. OenLDAP 配置记录
  7. SQLite 性能优化
  8. camunda流程定义表无数据_[Python04] 学习snakemake,三步轻松搭建生信流程!
  9. SSH中为什么action需要用多例而dao层和service层为什么就用单例就可以
  10. Windows搭建SonarQube_Mysql
  11. 【Java】探究Java实现多接口时同名方法冲突问题
  12. hbuild 编译 php,Hbuilder的PHP环境搭建
  13. Docker问题:Dockerfile的From之前不能使用ARG
  14. ZetCode 杂项教程
  15. Directory Opus一款功能强大的资源管理器
  16. 【Unity3D】资源文件 ③ ( Unity 资源包简介 | 导出 Unity 资源包 | 导出资源包的包含依赖选项 | 导入 Unity 资源包 | Unity 资源商店 )
  17. web性能测试的关注点
  18. linux boot引导修复工具,修复linux的grub2引导(单独/boot,lvm-root)
  19. 论文中的参考文献规范
  20. The First Paper

热门文章

  1. 怎么用云服务器搭建游戏,搭建游戏用什么云服务器
  2. flutter 刷脸_传说哥教你如何假装架构师
  3. IPSec 密钥加密体系概述
  4. macos无法使用sudo_如何在macOS上使用Touch ID运行Sudo命令
  5. 运用CNN对ImageNet进行图像分类
  6. 入门vue+springboot项目
  7. 神经网络的基本骨架-nn.Moudle的使用
  8. 集合下现在淘宝小号所遇见的常见问题
  9. linux卸载带输入法,Ubuntu删除自带的输入法之后设置不见了
  10. python实现模糊综合评判