Daily Pathtracer!安利下不错的Pathtracer学习资料
0x00 前言
最近看到了我司大网红aras-p(Aras Pranckevičius)的博客开了一个很有趣的新系列《Daily Pathtracer~》,来实现一个简单的ToyPathTracer。

除了使用C++,aras还使用了两种不同的C#运行时来实现,即.NET Standard和Xamarin/Mono。效率上自然C++要强于.NET Standard,而.NET Standard的表现要强于Xamarin/Mono。
除此之外,aras当然还会用到Unity来实现,比较有意思的是Unity的实现中使用了2018中的Burst来提升性能,而结果甚至有点小惊人呢,之后可以再说。
而aras的这个小巧的ToyPathTracer事实上和最近在知乎上小火的小书《Ray Tracing in One Weekend》还有些关系。其实这是一套3本中的第一本书,另外还有两本,分别叫《Ray Tracing: the Next Week》和《Ray Tracing: The Rest Of Your Life》。

嗯,真是一入码门深似海。而图形学作为程序员的三大浪漫之一,真是诚不我欺也。
当然,这本书的作者Peter Shirley已经将此书作为PDF免费放出了(此处有地址:https://drive.google.com/drive/folders/14yayBb9XiL16lmuhbYhhvea8mKUUK77W),但是各位如果有兴趣的话建议还是买一下吧(seriously, just buy that minibook)。
所以前言部分先安利的,是这套小书。
0x01 Unity中实现PathTracer
aras的这一系列博客很长,目录摘录在下面。
- Part 0: Intro
- Part 1: Initial C++ and walkthrough
- Part 2: Fix stupid performance issue
- Part 3: C#, Unity and Burst
- Part 4: Correctness fixes and Mitsuba
- Part 5: simple GPU version via Metal
- Part 6: simple GPU version via D3D11
- Part 7: initial C++ SIMD & SoA
- Part 8: SSE SIMD for HitSpheres
- Part 9: ryg optimizes my code
- Part 10: Update all implementations to match
- Part 11: Buffer-oriented approach on CPU
- Part 12: Buffer-oriented approach on GPU D3D11
- Part 13: GPU thread group data optimization
我的这篇小文作为安利文,也就不详细介绍了每一篇的内容了,相信有兴趣的小伙伴都会去看的。
但是其中有一篇文章还是蛮有趣的,因为对比了不同语言、不同运行时的性能差异,而且还演示了一下Unity2018中的Burst compiler对C#代码性能的提升。所以稍微介绍下这部分吧。
当然,首先可以看到,场景是作为硬编码写死的:
static Sphere[] s_SpheresData = {
new Sphere(new float3(0,-100.5f,-1), 100),
new Sphere(new float3(2,0,-1), 0.5f),
new Sphere(new float3(0,0,-1), 0.5f),
new Sphere(new float3(-2,0,-1), 0.5f),
new Sphere(new float3(2,0,1), 0.5f),
new Sphere(new float3(0,0,1), 0.5f),
new Sphere(new float3(-2,0,1), 0.5f),
new Sphere(new float3(0.5f,1,0.5f), 0.5f),
new Sphere(new float3(-1.5f,1.5f,0f), 0.3f),
#if DO_BIG_SCENE
new Sphere(new float3(4,0,-3), 0.5f), new Sphere(new float3(3,0,-3), 0.5f), new Sphere(new float3(2,0,-3), 0.5f), new Sphere(new float3(1,0,-3), 0.5f), new Sphere(new float3(0,0,-3), 0.5f), new Sphere(new float3(-1,0,-3), 0.5f), new Sphere(new float3(-2,0,-3), 0.5f), new Sphere(new float3(-3,0,-3), 0.5f), new Sphere(new float3(-4,0,-3), 0.5f),
new Sphere(new float3(4,0,-4), 0.5f), new Sphere(new float3(3,0,-4), 0.5f), new Sphere(new float3(2,0,-4), 0.5f), new Sphere(new float3(1,0,-4), 0.5f), new Sphere(new float3(0,0,-4), 0.5f), new Sphere(new float3(-1,0,-4), 0.5f), new Sphere(new float3(-2,0,-4), 0.5f), new Sphere(new float3(-3,0,-4), 0.5f), new Sphere(new float3(-4,0,-4), 0.5f),
new Sphere(new float3(4,0,-5), 0.5f), new Sphere(new float3(3,0,-5), 0.5f), new Sphere(new float3(2,0,-5), 0.5f), new Sphere(new float3(1,0,-5), 0.5f), new Sphere(new float3(0,0,-5), 0.5f), new Sphere(new float3(-1,0,-5), 0.5f), new Sphere(new float3(-2,0,-5), 0.5f), new Sphere(new float3(-3,0,-5), 0.5f), new Sphere(new float3(-4,0,-5), 0.5f),
new Sphere(new float3(4,0,-6), 0.5f), new Sphere(new float3(3,0,-6), 0.5f), new Sphere(new float3(2,0,-6), 0.5f), new Sphere(new float3(1,0,-6), 0.5f), new Sphere(new float3(0,0,-6), 0.5f), new Sphere(new float3(-1,0,-6), 0.5f), new Sphere(new float3(-2,0,-6), 0.5f), new Sphere(new float3(-3,0,-6), 0.5f), new Sphere(new float3(-4,0,-6), 0.5f),
new Sphere(new float3(1.5f,1.5f,-2), 0.3f),
#endif // #if DO_BIG_SCENE
};
static Material[] s_SphereMatsData = {
new Material(Material.Type.Lambert, new float3(0.8f, 0.8f, 0.8f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Lambert, new float3(0.8f, 0.4f, 0.4f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Lambert, new float3(0.4f, 0.8f, 0.4f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Metal, new float3(0.4f, 0.4f, 0.8f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Metal, new float3(0.4f, 0.8f, 0.4f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Metal, new float3(0.4f, 0.8f, 0.4f), new float3(0,0,0), 0.2f, 0),
new Material(Material.Type.Metal, new float3(0.4f, 0.8f, 0.4f), new float3(0,0,0), 0.6f, 0),
new Material(Material.Type.Dielectric, new float3(0.4f, 0.4f, 0.4f), new float3(0,0,0), 0, 1.5f),
new Material(Material.Type.Lambert, new float3(0.8f, 0.6f, 0.2f), new float3(30,25,15), 0, 0),
#if DO_BIG_SCENE
new Material(Material.Type.Lambert, new float3(0.1f, 0.1f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.2f, 0.2f, 0.2f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.3f, 0.3f, 0.3f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.4f, 0.4f, 0.4f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.5f, 0.5f, 0.5f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.6f, 0.6f, 0.6f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.7f, 0.7f, 0.7f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.8f, 0.8f, 0.8f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.9f, 0.9f, 0.9f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Metal, new float3(0.1f, 0.1f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.2f, 0.2f, 0.2f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.3f, 0.3f, 0.3f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.4f, 0.4f, 0.4f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.5f, 0.5f, 0.5f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.6f, 0.6f, 0.6f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.7f, 0.7f, 0.7f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.8f, 0.8f, 0.8f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.9f, 0.9f, 0.9f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Metal, new float3(0.8f, 0.1f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.8f, 0.5f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.8f, 0.8f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.4f, 0.8f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.1f, 0.8f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.1f, 0.8f, 0.5f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.1f, 0.8f, 0.8f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.1f, 0.1f, 0.8f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.5f, 0.1f, 0.8f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Lambert, new float3(0.8f, 0.1f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.8f, 0.5f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.8f, 0.8f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.4f, 0.8f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.1f, 0.8f, 0.1f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.1f, 0.8f, 0.5f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.1f, 0.8f, 0.8f), new float3(0,0,0), 0, 0), new Material(Material.Type.Lambert, new float3(0.1f, 0.1f, 0.8f), new float3(0,0,0), 0, 0), new Material(Material.Type.Metal, new float3(0.5f, 0.1f, 0.8f), new float3(0,0,0), 0, 0),
new Material(Material.Type.Lambert, new float3(0.1f, 0.2f, 0.5f), new float3(3,10,20), 0, 0),
#endif
};
当然需要来判断是否相交的:
static bool HitWorld(Ray r, float tMin, float tMax, ref Hit outHit, ref int outID, ref SpheresSoA spheres)
{
outID = spheres.HitSpheres(ref r, tMin, tMax, ref outHit);
return outID != -1;
}
光线的追踪:
static float3 Trace(Ray r, int depth, ref int inoutRayCount, ref SpheresSoA spheres, NativeArray<Material> materials, ref uint randState, bool doMaterialE = true)
{
Hit rec = default(Hit);
int id = 0;
++inoutRayCount;
if (HitWorld(r, kMinT, kMaxT, ref rec, ref id, ref spheres))
{
Ray scattered;
float3 attenuation;
float3 lightE;
var mat = materials[id];
var matE = mat.emissive;
...
物体表面如何处理接收到的光线呢?可以看这里:
static bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray scattered, out float3 outLightE, ref int inoutRayCount, ref SpheresSoA spheres, NativeArray<Material> materials, ref uint randState)
{
outLightE = new float3(0, 0, 0);
if (mat.type == Material.Type.Lambert)
{
// random point inside unit sphere that is tangent to the hit point
float3 target = rec.pos + rec.normal + MathUtil.RandomUnitVector(ref randState);
scattered = new Ray(rec.pos, normalize(target - rec.pos));
attenuation = mat.albedo;
// sample lights
...
最后,在Unity中使用了burst,并分发了任务:
[ComputeJobOptimization]
struct TraceRowJob : IJobParallelFor
{
public int screenWidth, screenHeight, frameCount;
[NativeDisableParallelForRestriction] public NativeArray<UnityEngine.Color> backbuffer;
public Camera cam;
[NativeDisableParallelForRestriction] public NativeArray<int> rayCounter;
[NativeDisableParallelForRestriction] public SpheresSoA spheres;
[NativeDisableParallelForRestriction] public NativeArray<Material> materials;
public void Execute(int y)
{
int backbufferIdx = y * screenWidth;
float invWidth = 1.0f / screenWidth;
float invHeight = 1.0f / screenHeight;
float lerpFac = (float)frameCount / (float)(frameCount + 1);
...
通过UnityProfiler,我们可以看到经过Burst优化后的运行状态:

不过让我感到比较吃惊的是,在Unity2018中使用C#+Burst时的效率竟然超过了C++的实现,看来Burst的效率还是很惊人的。
下面是aras那里的结论:
- .NET Core is about 2x slower than vanilla C++.
- Mono (with default settings) is about 3x slower than .NET Core.
- IL2CPP is 2x-3x faster than Mono, which is roughly .NET Core performance level.
- Unity’s Burst compiler can get our C# code faster than vanilla C++. Note that right now Burst is very early tech, I expect it will get even better performance later on.
当然,aras的博客中有更详细的对比数据。
所以这部分安利的,是aras的博客(http://aras-p.info/blog/2018/03/28/Daily-Pathtracer-Part-0-Intro/)。
0x02 小结
当然,aras除了写了博客之外,也开源了这个ToyPathTracer(也许先有了ToyPathTracer才有了这系列的博客也未可知)。
所以各位可以去github上clone一份代码,除了可以分别在windows和mac上编译c++、c#的实现,也可以直接在unity中使用。

所以最后安利一下这个工程(https://github.com/aras-p/ToyPathTracer)。
怎么样,是不是还挺有趣的?
Ref
《Ray Tracing in One Weekend》
https://drive.google.com/drive/folders/14yayBb9XiL16lmuhbYhhvea8mKUUK77W
《Daily Pathtracer》
http://aras-p.info/blog/2018/03/28/Daily-Pathtracer-Part-0-Intro/
《ToyPathTracer》
https://github.com/aras-p/ToyPathTracer
-EOF-
最后打个广告,欢迎支持我的书《Unity 3D脚本编程》

欢迎大家关注我的公众号慕容的游戏编程:chenjd01

Daily Pathtracer!安利下不错的Pathtracer学习资料的更多相关文章
- 很不错的jQuery学习资料和实例
这些都是学习Jquery很不错的资料,整理了一下,分享给大家. 希望能对大家的学习有帮助. 帕兰 Noupe带来的51个最佳jQuery教程和实例, 向大家介绍了jQuery的一些基本概念和使用的相关 ...
- 一个不错的linux学习资料下载的网址
本文比较完整的讲述GNU make工具,涵盖GNU make的用法.语法.同时重点讨论如何为一个工程编写Makefile.作为一个Linux程序员,make工具的使用以及编写Makefile是必需的. ...
- GitHub上的计算机视觉学习资料推荐
9月份将要读研,导师是做cv的,最近学习时找到了不少的计算机视觉的资料,记录一下,同时也分享给需要的朋友 assmdx/ComputerVisionDoc AceCoooool/interview-c ...
- Linux下C编程的学习_1
0x0:为什么写这个系列的文章 博客原本的定位是安卓游戏的破解,可是为什么写这系列的文章呢? 由于在破解过程中,我们是无法避免来敲代码的,恢复算法,模拟算法,游戏中对数据的解密.游戏中对保存在clie ...
- MVC不错的学习资料
MVC不错的学习资料: http://www.cnblogs.com/darrenji/
- (转)NoSQL——Redis在win7下安装配置的学习一
NoSQL——Redis在win7下安装配置的学习一 有些也是从网上看来的 1.下载安装 Redis它没有windows的官方版本,但是又非官方的版本,到官网上去下载相应的版本,我的电脑是win7 ...
- linux下的IO模型---学习笔记
1.linux文件系统和缓存 文件系统接口 文件系统-一种把数据组织成文件和目录的存储方式,提供了基于文件的存取接口,并通过文件权限控制访问. 存储层次 文件系统缓存 主存(通常时DRAM)的一块区域 ...
- Android 学习资料收集
收集整理这份资料灵感来自于 trip_to_iOS, 征得同意引用了该资料的开头描述 收集整理这份资料主要帮助初学者学习 Android 开发, 希望能快速帮助到他们快速入门, 找到适合自己学习资料, ...
- iOS学习资料整理
视频教程(英文) 视频 简介 Developing iOS 7 Apps for iPhone and iPad 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS S ...
随机推荐
- Sprite子节点透明度不能跟随父节点变化的问题求解(转)
原出处忘记了. [已解决]Sprite子节点透明度不能跟随父节点变化的问题求解 自己封装了一个按钮控件,点击的时候封装了一些动作,其中有透明度的变化. 当点击发生的时候,Sprite本体执行正常,但是 ...
- C#来操作Word
创建Word: 插入文字,选择文字,编辑文字的字号.粗细.颜色.下划线等: 设置段落的首行缩进.行距: 设置页面页边距和纸张大小: 设置页眉.页码: 插入图片,设置图片宽高以及给图片添加标题: 插入表 ...
- Mybatis自定义分布式二级缓存实现与遇到的一些问题解决方案!
先说两句: 我们都知道Mybatis缓存分两类: 一级缓存(同一个Session会话内) & 二级缓存(基于HashMap实现的以 namespace为范围的缓存) 今天呢, 我们不谈一级缓存 ...
- Linux支持ntfs,exfat格式文件系统
sudo apt-get install exfat-utilssudo apt-get install ntfs-3g ntfs-config exFAT最高支持16EB的文件,并且exfat在wi ...
- 在GNU/Linux下制作Windows 10安装U盘
今年春节回家期间,我需要将家里的一台安装了Debian Stretch的ZaReason笔记本电脑更换为Windows 10系统,好让爸妈从老台式机上的XP系统升级到新的平台上来.回家前,小仙女已在微 ...
- PHP将图片转base64编码以及base64图片转换为图片并保存代码
图片转base64编码 /*图片转换为 base64格式编码*/ $img = 'uploads/01.png'; $base64_img = base64EncodeImage($img); ech ...
- MyCat分片规则--笔记(二)
概述 myCat实现分库分表的策略,对数据量的处理带来很大的便利,这里主要整理下MyCat的使用以及常用路由算法,针对MyCat里面的事务.集群后续再做整理:另外内容整理,不免会参考技术大牛的博客,内 ...
- 当TFS/VSTS遇上Power BI
引言 众所周知,要对TFS进行深入的图表分析,往往需要依赖于SQL Server Analysis Service和SQL Server Reporting Service.虽然随着TFS对敏捷项目的 ...
- 做IT,必备的安全知识!
以前的认知 以前刚接触IT行业,而我身为运维,我以为我所需要做的安全就是修改服务器密码为复杂的,ssh端口改为非22,还有就是不让人登录服务器就可以保证我维护的东西安全. 现在的认知 工作也好几年了, ...
- docker使用教程
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Li ...