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. elastalert 配置post告警方式(备忘)

      最近在做把elk告警日志发送到kinesis 流,供后续数据分析处理使用........ 基于尽量不修改elastalert ,把修改工作放到接收端服务的原则.计划把elk的告警数据通过远程api ...

  2. Linux基本命令总结(四)

    接上篇: 16,locate 让使用者可以很快速的搜寻档案系统内是否有指定的档案.其方法是先建立一个包括系统内所有档案名称及路径的数据库,之后当寻找时就只需查询这个数据库,而不必实际深入档案系统之中了 ...

  3. bzoj4170 极光

    题目链接 题面 题意 把每个位置的点都看成是一个二维坐标系中的点.比如第\(i\)个点就是\((i,a[i])\). 有两种操作 询问:然后每次询问的就是与当前点坐标的曼哈顿距离小于等于\(k\)的点 ...

  4. phpmyadmin低权限getshell

    账号:‘localhost’@'@” 密码:为空 可获得一个低权限账号 利用方法: Mysql可以把指定的文件写进表 CREATE TABLE `test`.`a` (`a1` TEXT NOT NU ...

  5. 集成学习—boosting和bagging

    集成~bagging~权值~组合~抽样~样例~基本~并行 一.简介 集成学习通过构建并结合多个学习器来完成学习任务,常可获得比单一学习器显著优越的泛化性能 根据个体学习器的生成方式,目前的集成学习方法 ...

  6. 主机管理+堡垒机系统开发:strace命令用法详解(六)

    一.简单介绍 strace是什么? 按照strace官网的描述, strace是一个可用于诊断.调试和教学的Linux用户空间跟踪器.我们用它来监控用户空间进程和内核的交互,比如系统调用.信号传递.进 ...

  7. HDU 5968(异或计算 暴力)

    题意是在一个数列中找到一段连续的子串使其异或值与所给值最接近,求出子串长度,若有多组结果,输出最大长度. 做题之前一定多注意数据范围,这道题就可以直接暴力,用数组 p[ i ][ j ] 表示长度为 ...

  8. 第六节:WebApi的部署方式(自托管)

    一. 简单说明 开篇就介绍过WebApi和MVC相比,其中优势之一就是WebApi可以不依赖于IIS部署,可以自托管,当然这里指的是 .Net FrameWork 下的 WebApi 和 MVC 相比 ...

  9. 第十四节: EF的三种模式(四) 之 原生正宗的 CodeFirst模式的默认约定

    一. 简介 1. 正宗的CodeFirst模式是不含有edmx模型,需要手动创建实体.创建EF上下文,然后生成通过代码来自动映射生成数据库. 2. 旨在:忘记SQL.忘记数据库. 3. 三类配置:On ...

  10. ArcGis安装失败提示“需要Microsoft .NET Framework 3.5 sp1或等效环境”的解决方法

    这个问题一般出现在Win8或者Win10系统上,因为系统默认没有启用该.Net Framework. 下载Microsoft .NET Framework 3.5 sp1安装后再开始安装ArcGis. ...