PBRT2与3之间的改动

  1. 增加了一个功能完备的BRDF模型,支持体积光照与重要性多重路径采样。
  2. 次表面散射,基于光线追踪技术,无需预处理。
  3. 解决浮点数四折五入的问题
  4. 光子映射
  5. 样本生成
  6. 第一章多了讲并行的东西

    看到第2页
渲染分块问题

对这个渲染任务过多的分块会影响性能。

场景的复杂性会对不同CPU核心的渲染速度产生影响。所以如果是分块数等于核心数,渲染完的核心会等待没渲染完的核心。

过小分块也是不科学的,因为处理核心问题有一定的开销。

pbrt里用的是16*16的方案

浮点类型

pbrt采用了FLOAT,这样浮点格式会根据宏进行调整

主循环
void SamplerIntegrator::Render(const Scene &scene) {
Preprocess(scene, *sampler);
//分块并行渲染图片 //计算tiles, _nTiles_,用于并行运算
Bounds2i sampleBounds = camera->film->GetSampleBounds();
Vector2i sampleExtent = sampleBounds.Diagonal();
//书中说16*16分块可以解决大部分情况
const int tileSize = 16;
//sampleExtent.x + tileSize - 1 整除考虑
Point2i nTiles((sampleExtent.x + tileSize - 1) / tileSize,
(sampleExtent.y + tileSize - 1) / tileSize);
ProgressReporter reporter(nTiles.x * nTiles.y, "Rendering");
{
ParallelFor2D([&](Point2i tile) {
// Render section of image corresponding to _tile_ //内存池,之后会给Li函数
MemoryArena arena; //给每个块分配一个Samper实例
int seed = tile.y * nTiles.x + tile.x;
std::unique_ptr<Sampler> tileSampler = sampler->Clone(seed); //计算块的采样边界
int x0 = sampleBounds.pMin.x + tile.x * tileSize;
int x1 = std::min(x0 + tileSize, sampleBounds.pMax.x);
int y0 = sampleBounds.pMin.y + tile.y * tileSize;
int y1 = std::min(y0 + tileSize, sampleBounds.pMax.y);
Bounds2i tileBounds(Point2i(x0, y0), Point2i(x1, y1));
LOG(INFO) << "Starting image tile " << tileBounds; //取得指定范围的图片块
std::unique_ptr<FilmTile> filmTile =
camera->film->GetFilmTile(tileBounds); //开始指定区域的渲染循环
for (Point2i pixel : tileBounds) {
{
ProfilePhase pp(Prof::StartPixel);
tileSampler->StartPixel(pixel);
} // Do this check after the StartPixel() call; this keeps
// the usage of RNG values from (most) Samplers that use
// RNGs consistent, which improves reproducability /
// debugging.
if (!InsideExclusive(pixel, pixelBounds))
continue; do {
//初始化相机采样,存储了时间以及镜头位置的采样值
CameraSample cameraSample =
tileSampler->GetCameraSample(pixel); //生成相机光线
//以及光线的pdf
RayDifferential ray;
Float rayWeight =
camera->GenerateRayDifferential(cameraSample, &ray);
ray.ScaleDifferentials(
1 / std::sqrt((Float)tileSampler->samplesPerPixel));
++nCameraRays; //估算当前相机光线辐射度
Spectrum L(0.f);
if (rayWeight > 0) L = Li(ray, scene, *tileSampler, arena); //对渲染出错误结果的处理,log相应信息
if (L.HasNaNs()) {
LOG(ERROR) << StringPrintf(
"Not-a-number radiance value returned "
"for pixel (%d, %d), sample %d. Setting to black.",
pixel.x, pixel.y,
(int)tileSampler->CurrentSampleNumber());
L = Spectrum(0.f);
} else if (L.y() < -1e-5) {
LOG(ERROR) << StringPrintf(
"Negative luminance value, %f, returned "
"for pixel (%d, %d), sample %d. Setting to black.",
L.y(), pixel.x, pixel.y,
(int)tileSampler->CurrentSampleNumber());
L = Spectrum(0.f);
} else if (std::isinf(L.y())) {
LOG(ERROR) << StringPrintf(
"Infinite luminance value returned "
"for pixel (%d, %d), sample %d. Setting to black.",
pixel.x, pixel.y,
(int)tileSampler->CurrentSampleNumber());
L = Spectrum(0.f);
}
VLOG(1) << "Camera sample: " << cameraSample << " -> ray: " <<
ray << " -> L = " << L; // Add camera ray's contribution to image
filmTile->AddSample(cameraSample.pFilm, L, rayWeight); //释放内存池
arena.Reset();
} while (tileSampler->StartNextSample());
}
LOG(INFO) << "Finished image tile " << tileBounds; //将图片块合并到图片上去
camera->film->MergeFilmTile(std::move(filmTile));
reporter.Update();
}, nTiles);
reporter.Done();
}
LOG(INFO) << "Rendering finished"; //保存图片到文件
camera->film->WriteImage();
}

并行相关问题

  1. 读取场景文件以及创建场景都是单线程的,获取场景信息因为不涉及到修改数据,所以可以无视。我们只需要关注修改内存数据的情况。
  2. 请不要在不同步的情况下修改数据
  3. 对应初始化可以考虑std::call_once函数
  4. 实用程序类MemoryArena(用于高性能临时内存分配)和RNG(伪随机数生成)也不适合多线程使用; 这些类存储在调用其方法时被修改的状态,并且相互排除的保护修改到其状态的开销相对于它们执行的计算量而言过多。 因此,在上面的SamplerIntegrator :: Render()方法的代码中,实现在堆栈上分配这些类的perthread实例。
  5. 每个线程需要各复制一个Sampler的实例,以保证线程安全
  6. 目前的计算机架构运算除法、平方根和三角函数是最慢的。加法与乘法相比之下要快10~50倍。所以我们可以减少这种数学运算数量来提高性能。例如我们可以提前计算1/v,之后再乘。而不是重复除以v。

Shape类

  1. Shape存储了Transform(正变换、逆变换)指针指针,这些指针都指向了一个智能指针,通过Transform池来进行内存空间管理。 (3.1.1)
  2. 全部Shape对象都采用了独立int32进行id管理(3.1.1)
  3. 在PBRT中的一些Shape支持通过texture的alpha通道颜色进行cutting away。(3.1.1)
  4. IntersectP()返回相交结果而不返回相交表面数据,Intersect()则会返回相交表面数据。(3.1.3)
  5. 为了使用面光源,所以会计算表面面积(面积模型)(3.1.4)
  6. PBRT不支持单面渲染(RayTracig from the ground 支持这个)(3.1.5)

sphere

使用一元二次方程求根很可能会因为浮点数舍入误差从而得到错误结果,一般来说,(-b)、sqrtdis 都是正数的话,-b+sqrtdis所得的正根一般都会是正确的,此时我们可以使用“维达定理”求得负根。

$ x_1x_2=\dfrac{c}{a} \Leftrightarrow x_2= \dfrac{c}{ax_1}$

关于舍入误差

static constexpr Float MaxFloat = std::numeric_limits<Float>::max();
static constexpr Float Infinity = std::numeric_limits<Float>::infinity();
std::numeric_limits<Float>::epsilon() * 0.5;
我们可以通过断言 !(x==x)来判断是否是NaN,也可以使用std::isnan。

光线追踪中的光线与物体求交函数的tmin值,可以帮助抵消因为浮点数舍入误差而造成的在物体内部相交的问题。较大的tmin值可以起到更好的效果,但是过大的tmin值,会使靠的比较近的面丢失部分反射与阴影效果。

不过PBRT告诉我们,可以通过一定的系统设计,让我们不再需要ray epsilon。(使用EFLoat类)

EFloat类通过重载计算符的方式,使用误差计算公式。使得EFLoat类之间的计算可以正确地积累或者抵消之前浮点运算的误差。

low = NextFloatDown(v - err);
high = NextFloatUp(v + err);

最新的代码直接计算出误差边界,而不是存储在float err变量。

PBRT笔记(1)——主循环、浮点误差的更多相关文章

  1. Cocos2d-x 3.2 学习笔记(十六)保卫萝卜 游戏主循环与定时器

    保卫萝卜~想法一直存在于想法,实战才是硬道理!有想法就去实现,眼高手低都是空谈.   一.游戏主循环GameSchedule      主循环是游戏处理逻辑,控制游戏进度的地方,处理好主循环是很重要的 ...

  2. PBRT笔记(6)——采样和重构

    前言 本文仅作为个人笔记分享,又因为本章涉及多个专业领域而本人皆未接触过,所以难免出错,请各位读者注意. 对于数字图像需要区分image pixels(特定采样处的函数值)和display pixel ...

  3. 游戏主循环(Game Loop)

    游戏主循环是游戏的心跳,一般使用while循环进行主动刷新. 一次循环由获取用户输入.更新游戏状态.处理AI.播放音乐和绘制画面组成. 这些行为可以分成两类: update_game(); // 更新 ...

  4. 辛巴学院-Unity-剑英的c#提高篇(一)主循环

    这是测试版 辛巴学院:正大光明的不务正业. 最近刚刚离开了我服务了三年多的公司,因为一个无数次碰到的老问题,没钱了. 之前不知道做什么好的时候,机缘巧合之下和哒嗒网络的吴总聊了一下,发现了vr gam ...

  5. 用WP_Query自定义WordPress 主循环

    我们知道操作 WordPress 主循环(WordPress Loop)最容易的方法是使用 query_posts 函数. 但是使用 query_posts 直接修改 WordPress 默认的主循环 ...

  6. Update主循环、状态机的实现

    从写一段程序,到写一个app,写一个游戏,到底其中有什么不同呢?一段程序的执行时间很短,一个应用的执行时间很长,仅此而已. 游戏中存在一个帧的概念.   这个概念大家都知道,类比的话,它就是电影胶卷的 ...

  7. POJ1064 Cable master(二分 浮点误差)

    题目链接:传送门 题目大意: 给出n根长度为1-1e5的电线,想要从中切割出k段等长的部分(不可拼接),问这个k段等长的电线最长可以是多长(保留两位小数向下取整). 思路: 很裸的题意,二分答案即可. ...

  8. [UE4]游戏主循环

    游戏的运行模型 理解游戏的运行模型,对处理很多游戏错误有非常大的帮助. 游戏是有一个主循环的.那么游戏主循环做了什么事情呢? 游戏主循环一次就表示一帧,游戏主循环包括:接受输入.处理游戏逻辑.渲染.S ...

  9. MYSQL 的 MASTER到MASTER的主主循环同步

    MYSQL 的 MASTER到MASTER的主主循环同步   刚刚抽空做了一下MYSQL的主主同步.把步骤写下来,至于会出现的什么问题,以后随时更新.这里我同步的数据库是TEST1.环境描述.   主 ...

随机推荐

  1. 使用svn进行协作开发

    环境 操作系统:win7 64位 所需工具 1. 服务器端(Subversion)[Setup-Subversion-1.8.16.msi] 2. 客户端(TortoiseSVN)[TortoiseS ...

  2. 【Java面试题】19 final,finally和finalize的区别

    总体区别 final       用于申明属性,方法和类,表示属性不可变,方法不可以被覆盖,类不可以被继承.finally     是异常处理语句结构中,表示总是执行的部分. finallize   ...

  3. Dynamics CRM 日常使用JS整理(一)

    整理下平时CRM开发中用到的一些基本的js操作 取值: var oResult = Xrm.Page.getAttribute(sFieldName).getValue(); var oResult ...

  4. 用vim打开.py和.sh文件自动添加头

    在~/.vimrc文件最后一行添加 "auto add pyhton header --start autocmd BufNewFile *.py 0r ~/.vim/template/py ...

  5. js值类型转换(boolean/String/number),js运算符,if条件,循环结构,函数,三种弹出框

    js值类型转换 number | string | boolean boolean类型转换 num = 0; var b1 = Boolean(num); console.log(b1) 转化为数字类 ...

  6. H5_0001:localStorage本地存储

    localStorage的优势 1.localStorage拓展了cookie的4K限制 2.localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数 ...

  7. Nginx虚拟主机 子文件单独配置

    上一篇所有的server 全都配置在nginx.conf配置文件里,其实每个server 都可以单独做一个子文件 删除nginx.conf配置文件中的server及其余内容,加上如下图 创建保存每个虚 ...

  8. ERROR [main] master.HMasterCommandLine Master exiting

    2018-05-18 07:07:26,257 INFO [main-SendThread(localhost:2181)] zookeeper.ClientCnxn: Opening socket ...

  9. MYSQL(三)

    转载自https://www.cnblogs.com/wupeiqi/articles/5716963.html 1.索引 索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数 ...

  10. Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC

    解决Invalid character found in the request target. The valid characters are defined in RFC 7230 and RF ...