Camera

class Camera {
public:
//实现相机在一定时间内进行特定的运动
AnimatedTransform CameraToWorld;
//快门开/关数据,可以用于计算动态模糊
const Float shutterOpen, shutterClose;
//胶片类指针用于计算最终图像
Film *film;
//介质类,表达相机所在的介质(在空气、水中)
const Medium *medium;
}

GenerateRay生成当前相机采样的一条光线,并且返回生成的光线对于最终图像的贡献值,一般都为1,除了有什么特殊计算

//除了与GenerateRay相同的功能外,还生成一个偏移一个UV像素的光线(RayDifferential)
//该操作与下一章采样有关
Float Camera::GenerateRayDifferential(const CameraSample &sample,
RayDifferential *rd) const {
Float wt = GenerateRay(sample, rd);
if (wt == 0) return 0; //取得偏移光线
//这里的代码和书中不同,书中是取得偏移一个像素的光线
Float wtx;
//原来初始化列表可以作为临时对象
for (Float eps : { .05, -.05 }) {
CameraSample sshift = sample;
sshift.pFilm.x += eps;
Ray rx;
wtx = GenerateRay(sshift, &rx);
rd->rxOrigin = rd->o + (rx.o - rd->o) / eps;
rd->rxDirection = rd->d + (rx.d - rd->d) / eps;
if (wtx != 0)
break;
}
if (wtx == 0)
return 0; Float wty;
for (Float eps : { .05, -.05 }) {
CameraSample sshift = sample;
sshift.pFilm.y += eps;
Ray ry;
wty = GenerateRay(sshift, &ry);
rd->ryOrigin = rd->o + (ry.o - rd->o) / eps;
rd->ryDirection = rd->d + (ry.d - rd->d) / eps;
if (wty != 0)
break;
}
if (wty == 0)
return 0; rd->hasDifferentials = true;
return wt;
}

ProjectiveCamera

ProjectiveCamera继承自Camera类,实现了投影相机的相关功能。

这里需要需要注意一下,屏幕空间坐标与光栅化坐标的y轴方向是不同的,PBRT的屏幕空间还是一个三维坐标,所以y轴是向上的。而光栅化,与贴图的uv坐标方向相同,朝下。

在构造函数中初始化了

//相机坐标转屏幕坐标矩阵与逆矩阵
Transform CameraToScreen, RasterToCamera;
//屏幕空间坐标转光栅化坐标矩阵与逆矩阵
Transform ScreenToRaster, RasterToScreen;
//镜头半径、焦距
Float lensRadius, focalDistance;
//首先将坐标原点从左下移动左上,之后除以屏幕宽度获得标准化的坐标(0~1),再乘以贴图分辨率,获得光栅化的坐标
ScreenToRaster =
Scale(film->fullResolution.x, film->fullResolution.y, 1) *
Scale(1 / (screenWindow.pMax.x - screenWindow.pMin.x),
1 / (screenWindow.pMin.y - screenWindow.pMax.y), 1) *
Translate(Vector3f(-screenWindow.pMin.x, -screenWindow.pMax.y, 0));

OrthographicCamera

继承自ProjectiveCamera类。通过Orthographic函数生成正交矩阵,再用于初始化父类的CameraToScreen矩阵。

这个矩阵的会将摄像机的近平面(zNear)映射为0,远平面(zFar)映射为1。并将近平面移动至于z=0位置,随后对场景在z坐标进行缩放,也就是将其映射到01范围。可以理解为将近平面与远平面之间的场景放入一个长方体(z为01)的区域中。

Transform Orthographic(Float zNear, Float zFar) {
return Scale(1, 1, 1 / (zFar - zNear)) * Translate(Vector3f(0, 0, -zNear));
}

之后计算differential rays,因为正交相机的所生成的光线都是平行的,所以只需要移动原点即可。

dxCamera = RasterToCamera(Vector3f(1, 0, 0));
dyCamera = RasterToCamera(Vector3f(0, 1, 0));

因此我们可以很容易将光栅上的点转化为相机空间上的光线。(原点的x,y是确定的,z为zNear,方向是<0,0,1>)

如需景深效果,则根据快门开启时间(CameraSample::time)对光线进行调整,进而对景深进行模拟。最终返回的光线会被调整为世界空间的(毕竟求交操作都是世界空间的)

代码中为什么要在焦点平面进行采样偏移呢?PBRT后面的景深部分会解释,请见373页,看了图就明白了

Float OrthographicCamera::GenerateRay(const CameraSample &sample,
Ray *ray) const {
ProfilePhase prof(Prof::GenerateCameraRay);
//计算光栅空间与摄像机空间的采样点
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
Point3f pCamera = RasterToCamera(pFilm);
*ray = Ray(pCamera, Vector3f(0, 0, 1));
//根据景深的相关参数改变光线
if (lensRadius > 0) {
//在一个区间为[-1,1]的圆形区域中进行均匀采样
Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens); //计算焦点平面上的点,首先计算焦距与方向分量z之间的比值(射线中的t),之后使用自定义的()操作符计算点位置
Float ft = focalDistance / ray->d.z;
Point3f pFocus = (*ray)(ft); //更新光线
ray->o = Point3f(pLens.x, pLens.y, 0);
ray->d = Normalize(pFocus - ray->o);
}
//通过时间计算开门打开时间,并且存入ray中
ray->time = Lerp(sample.time, shutterOpen, shutterClose);
ray->medium = medium;
//转化为世界坐标
*ray = CameraToWorld(*ray);
return 1;
}
//对应版本的GenerateRayDifferential多了以下操作
if (lensRadius > 0) {
Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens);
Float ft = focalDistance / ray->d.z; //焦距平面上的偏移的点,并且分别计算x,y轴上的偏移数据
Point3f pFocus = pCamera + dxCamera + (ft * Vector3f(0, 0, 1));
ray->rxOrigin = Point3f(pLens.x, pLens.y, 0);
ray->rxDirection = Normalize(pFocus - ray->rxOrigin); pFocus = pCamera + dyCamera + (ft * Vector3f(0, 0, 1));
ray->ryOrigin = Point3f(pLens.x, pLens.y, 0);
ray->ryDirection = Normalize(pFocus - ray->ryOrigin);
} else {
ray->rxOrigin = ray->o + dxCamera;
ray->ryOrigin = ray->o + dyCamera;
ray->rxDirection = ray->ryDirection = ray->d;
}

PerspectiveCamera

继承自ProjectiveCamera类。通过Perspective函数生成透视矩阵。

透视矩阵将相机空间上的点投影到图像平面上。投影点的x'、y'为相机空间中的x、y除以z坐标。投影后z'就被正规化为[0,1]。

//f为远剪裁面,n为近剪裁面
Transform Perspective(Float fov, Float n, Float f) {
// Perform projective divide for perspective projection
Matrix4x4 persp(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, f / (f - n), -f * n / (f - n),
0, 0, 1, 0); //以弧度模式计算fov的tan值的倒数,再对之前计算的透视矩阵进行缩放
Float invTanAng = 1 / std::tan(Radians(fov) / 2);
return Scale(invTanAng, invTanAng, 1) * Transform(persp);
}
Float PerspectiveCamera::GenerateRay(const CameraSample &sample,
Ray *ray) const {
ProfilePhase prof(Prof::GenerateCameraRay);
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
//这里和OrthographicCamera略有不同其他的都一样
Point3f pCamera = RasterToCamera(pFilm);
*ray = Ray(Point3f(0, 0, 0), Normalize(Vector3f(pCamera)));
// Modify ray for depth of field
if (lensRadius > 0) {
Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens); Float ft = focalDistance / ray->d.z;
Point3f pFocus = (*ray)(ft); ray->o = Point3f(pLens.x, pLens.y, 0);
ray->d = Normalize(pFocus - ray->o);
}
ray->time = Lerp(sample.time, shutterOpen, shutterClose);
ray->medium = medium;
*ray = CameraToWorld(*ray);
return 1;
}

对应的GenerateRayDifferential也和OrthographicCamera的差不多,仅仅是焦点平面上的采样点的计算有些改变。

景深

理想的小孔摄像机只允许光线通过一个点达到胶片。而现实世界中是不存在这种相机的。但是可以制作光圈非常小的相机来模拟,光圈较小的相机需要较大的曝光时间以准确成像,相关较大光圈的相机在快门打开时会导致照片中快速移动的物体变得模糊。同时光圈越大,景深越明显,同时曝光所需时间越短。

对于场景中的一点z,以及从焦距为f的薄镜头中观察该点所得的一点z'。存在以下关系公式:

$ \frac{1}{z'}- \frac{1}{z}= \frac{1}{f}\(<br>
注意:当z=-∞时,z'=f。<br>
使用以下公式,在已知焦距与z的情况下计算z'。<br>
\)z'=\frac{fz}{f+z}$

弥散圆

引用自百度百科 在焦点前后,光线开始聚集和扩散,在一定范围外影象会变得模糊,形成一个扩大的圆,这个圆就叫做弥散圆。在现实当中,观赏拍摄的影象是以某种方式(比如投影、放大成照片等等)来观察的,人的肉眼所感受到的影象与放大倍率、投影距离及观看距离有很大的关系,如果弥散圆的直径小于人眼的鉴别能力,在一定范围内实际影象产生的模糊是不能辨认的。这个不能辨认的弥散圆就称为容许弥散圆(permissible circle of confusion)。

弥散圆受到光圈直径、焦距、物体与透镜之间的距离影响。

计算弥散圆的大小:观察书中图6.11可知几个变量直接的比例关系,得方程:

\(\frac{d_1}{z'}=\frac{d_c}{\left|z'-z_f'\right|}\)

经过整理:

\(d_c=\left|\frac{d_1(z'-z_f')}{z'} \right|\)

转换为场景深度后:

$ d_c=\left|\frac{d_1f(z-z_f)}{z(f+z_f)} \right|$

对简单镜头的模拟

光线追踪中对于简单镜头的建模如下:首先在镜头上选取一点,之后以该位置为起点,获取一条能使场景中处于对焦平面的物体,聚焦到胶片上的光线。(图6.13)

对于理想的小孔相机,对于胶片上的每一点(可以看成是一个像素)它的小孔透镜只准许一条光线通过。也就是说同一时间到达胶片上每一点的光线只有一条。

对于有限光圈的相机模型,我们先为每条光线在镜头上的圆盘区域中取得一个采样点,然后再计算穿过镜头中心并且使得对焦平面物体于胶片聚焦的光线。因为对焦平面的物体必然是聚焦的。所以光线始于采样点,相交于对焦平面。

环境相机

这里有用运用到球形坐标映射的问题,请见pbrt 5.5.2章。

GenerateRay将摄像机二维的采样点转化为三维的光线。

真实相机

这个挺复杂,直接跳过了

PBRT笔记(5)——相机模型的更多相关文章

  1. PBRT笔记(7)——反射模型

    基础术语 表面反射可以分为4大类: diffuse 漫反射 glossy specular 镜面反射高光 perfect specular 完美反射高光 retro-reflective distri ...

  2. 【双目备课】《学习OpenCV第18章》相机模型与标定整编

    一.相机模型 针孔模型.在这个简单模型中,想象光线是从场景或一个很远的物体发射过来的,但只有一条光线从该场景中的任意特定点进入针孔. 我们将这个图像进行抽象,就能够得到这样的结果: 其中,f为像到针孔 ...

  3. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  4. SLAM入门之视觉里程计(2):相机模型(内参数,外参数)

    相机成像的过程实际是将真实的三维空间中的三维点映射到成像平面(二维空间)过程,可以简单的使用小孔成像模型来描述该过程,以了解成像过程中三维空间到二位图像空间的变换过程. 本文包含两部分内容,首先介绍小 ...

  5. V-rep学习笔记:机器人模型创建3—搭建动力学模型

    接着之前写的V-rep学习笔记:机器人模型创建2—添加关节继续机器人创建流程.如果已经添加好关节,那么就可以进入流程的最后一步:搭建层次结构模型和模型定义(build the model hierar ...

  6. V-rep学习笔记:机器人模型创建2—添加关节

    下面接着之前经过简化并调整好视觉效果的模型继续工作流,为了使模型能受控制运动起来必须在合适的位置上添加相应的运动副/关节.一般情况下我们可以查阅手册或根据设计图纸获得这些关节的准确位置和姿态,知道这些 ...

  7. ArcGIS模型构建器案例学习笔记-字段处理模型集

    ArcGIS模型构建器案例学习笔记-字段处理模型集 联系方式:谢老师,135-4855-4328,xiexiaokui@qq.com 由四个子模型组成 子模型1:判断字段是否存在 方法:python工 ...

  8. 笔记-编程-IO模型

    笔记-编程-IO模型 1.      简介 常用IO模型 1)      同步阻塞IO(Blocking IO) 2)      同步非阻塞IO(Non-blocking IO) 3)      IO ...

  9. 运维开发笔记整理-Django模型语法

    运维开发笔记整理-Django模型语法 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.模型基本概念 1>.什么是模型 模型是你的数据唯一的,权威的信息源.它包含你所存储数 ...

随机推荐

  1. git添加删除远程tag

    git tag -a test20190108_1 -m "fix bug" git push origin test20190108_1 git push origin :ref ...

  2. React 记录(7)

    React文档:https://www.reactjscn.com/docs/handling-events.html 慢慢学习:对照教程文档,逐句猜解,截图 React官网:https://reac ...

  3. Java子线程中操作主线程Private级别数据

    两个类分别如下: <pre name="code" class="java">package Demo2; import java.util.*; ...

  4. Git以及TortoiseGit的下载安装使用

    Git以及TortoiseGit的下载安装使用 下载git 下载地址:https://git-scm.com/然后进行一系列的安装,傻瓜式的操作即可 TortoiseGit Tortoise 英[ˈt ...

  5. 微信小程序 mpvue vant

    Mpvue中使用Vant Weapp组件库 https://segmentfault.com/a/1190000016228410?utm_source=tag-newest 小程序采坑记 mpvue ...

  6. Beamer 中的页面链接

    \documentclass[]{beamer} \usetheme{Madrid} \usenavigationsymbolstemplate{} \title{Main Title} \autho ...

  7. Coursera, Big Data 3, Integration and Processing (week 4)

    Week 4 Big Data Precessing Pipeline 上图可以generalize 成下图,也就是Big data pipeline some high level processi ...

  8. Coursera, Big Data 2, Modeling and Management Systems (week 1/2/3)

    Introduction to data management 整个coures 2 是讲data management and storage 的,主要内容就是分布式文件系统,HDFS, Redis ...

  9. 【webpack】中file-loader和url-loader使用方法

    file-loader 可以指定要复制和放置资源文件的位置,以及如何使用版本哈希命名以获得更好的缓存.此外,这意味着 你可以就近管理图片文件,可以使用相对路径而不用担心部署时 URL 的问题.使用正确 ...

  10. safari中input、textarea无法输入的问题

    网址:https://www.cnblogs.com/xiayu25/p/6832748.html * { -webkit-box-sizing: border-box; -moz-box-sizin ...