概述
目录
一、光线追踪
光线
求光线与物体表面的交点
包围核加速
遍历所有的像素生成光线
二、加速结构
Spatial Partitions(空间划分)
Object Partition & Bounding Volume Hierarchy(BVH)
三、辐射度量学
四、光线传播和全局光照
Monte Carlo Integration蒙特卡洛积分
光线追踪
俄罗斯轮盘赌
一、光线追踪
光栅化不能将全局效果处理的很好,所以使用光线追踪
由于光栅化的算法原理,有很多表现上的限制,比如:
- 无法表现真实的软阴影
- 无法表现毛玻璃等粗糙的反射效果
- 无法表现非直接光照(只能用全局光照代替)
光线
- 光线沿直线传播
- 光线和光线之间不会发生碰撞,各传播各的
- 光线从光源中被发出来经过多次反射折射等,最后传入到eye中。也可以认为光路是可逆的,从eye发出感知光线可以原路返回到光源
- 光线追踪就是在模拟光线不断弹射的过程
- 将光线折射和反射出去所有地方用light进行计算一遍(算是否在shadow中),将所有点的着色的值加上
基于图形学中的“光路可逆性”而言,为了效率,渲染时采取的方法正是从摄像机发出“感知光线”进行渲染。
光线的定义:就是射线,一个点和一个方向。r(t)是光线上的任意一点
求光线与物体表面的交点
显式表面我们通过三角形网格来描述,那么问题就转换成为光线与三角形求交
通过三角形三个顶点定义平面,然后与射线相交,满足系数b1,b2,(1-b1,b2)均大于0,即存在三角形内交点,具体求解方式为克莱姆法则解线性方程组。
包围核加速
Bounding Box(AABB Axis-Aligned Bounding Box,轴对齐包围盒)
计算出进入三个对面的时间和出三个对面的时间,三个对面都进入了才算进入立方体,出去一个就算离开了。所以进入立方体的时间是进入三个对面的最大时间,离开立方体的时间是离开三个对面的最小时间。
在三维中,光线进入AABB的时间,是同时进入三组对面的时间,光线出AABB的时间,是出任意一组对面的时间。所以,光线进行AABB的时间,是最晚的tmin时间,tenter=max{tmin},出AABB的时间,texit=min{tmax},是tmax最早的时间,如果tenter<texit,说明光线在AABB中存在了一段时间,即可能与包围核中的物体存在交点。有几种特殊情况
- texit <0 ,AABB在光源后面
- texit >=0 && tenter < 0,光源在AABB中
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
const std::array<int, 3>& dirIsNeg) const
{
// invDir: ray direction(x,y,z), invDir=(1.0/x,1.0/y,1.0/z), use this because Multiply is faster that Division
// dirIsNeg: ray direction(x,y,z), dirIsNeg=[int(x>0),int(y>0),int(z>0)], use this to simplify your logic
// TODO test if ray bound intersects
float x_min = (pMin.x - ray.origin.x) * invDir[0];
float y_min = (pMin.y - ray.origin.y) * invDir[1];
float z_min = (pMin.z - ray.origin.z) * invDir[2];
float x_max = (pMax.x - ray.origin.x) * invDir[0];
float y_max = (pMax.y - ray.origin.y) * invDir[1];
float z_max = (pMax.y - ray.origin.y) * invDir[2];
if (!dirIsNeg[0])
{
swap(x_min, x_max);
}
if (!dirIsNeg[1])
{
swap(y_min, y_max);
}
if (!dirIsNeg[2])
{
swap(z_min, z_max);
}
float t_enter = max(x_min, max(y_min, z_min));
float t_exit = min(x_max, min(y_max, z_max));
return t_enter < t_exit && t_exit >= 0;
}
补:
遍历所有的像素生成光线
渲染器的目的是为帧的每个像素分配颜色
关于生成光线,我们需要的是找到这些像素在栅格空间(raster space)中的坐标与在世界空间(world space)中表达的相同像素的坐标之间的关系。
首先需要用每一帧的大小归一化(normalize)像素位置(pixel position),新的归一化的像素坐标被定义在规范化设备坐标系(NDC space):
我们在像素位置上增加了一个小位移(0.5),因为我们想让最终的相机光线通过像素的中间。
二、加速结构
Spatial Partitions(空间划分)
空间划分的最简单形式:Uniform Grids (均匀划分)。把空间均匀划分成若干相等大小的格子,记录每个格子内是否存在物体表面,然后光线穿过场景,判断沿途的格子是否存在物体表面:若存在,判断是否与物体表面相交;若不存在,continue。
如果太大,极端条件就是一整块,那就没有加速效果;如果太小,空间中存在大量立方块,那就会增加非常多的运算。
3D应该划分为:cell = 27*objs
不均匀划分
第一种,最直观的,oct-tree,八叉树,每一个格子按照八份的方式划分下去,直到格子里有足够少的物体或者没有物体为止。
第二种,bsp-tree,每次对空间二分,分割的面方向可以随意,分割面的任意性破坏了AABB的性质。
第三种,kd-tree,课程重点介绍的加速结构,每次砍一刀
使用kd-tree建立加速结构有以下特性
- 分割平面沿轴分割
- 二叉树形状
- 所有的物体,存储在叶子节点上
光线穿过空间,依次和每一层节点求交,若和某个节点不相交,那么光线就不会和以这个节点为根节点的子树上的所有节点相交;如果和某个节点相交,那么就需要继续判断是否和其左右子节点相交,直到遍历到叶子节点和光线相交,进而判断叶子节点里的物体和光线是否相交即可。
Object Partition & Bounding Volume Hierarchy(BVH)
BVH的构建方法:
- 找到一个包围核
- 按照某种方法,把包围核内的物体分成两个部分
- 两个部分重新计算包围核
- 然后按照kd-tree的思想,按照不同维度循环二分递归,直到满足中止条件(物体数量足够小)
- 把物体存在每个叶子节点上,其他的节点均用来加速判断
空间划分的每个空间是没有交集,但物体可能跨越多个空间;物体划分的空间可能存在交集,但是物体只存在于一个空间内。
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
// TODO Traverse the BVH to find intersection
Intersection myIntersection;
if (node == nullptr || !node->bounds.IntersectP(ray, ray.direction_inv, { 0,0,0 }))
return myIntersection;
if (node->left == nullptr && node->right == nullptr)
{
myIntersection = node->object->getIntersection(ray);
return myIntersection;
}
Intersection l1, l2;
l1 = getIntersection(node->left, ray);
l2 = getIntersection(node->right, ray);
myIntersection = l1.distance <= l2.distance ? l1 : l2;
return myIntersection;
}
三、辐射度量学
辐射度量学定义了一套光照物理模型,是光线追踪的基础。
基本概念:Radiant Energy and Flux (Power) 能量和功率
定义:Radiant Energy 电磁辐射的能量(光源辐射出的能量) Q[J=Joule]
定义:Radiant Flux (Power) :energy per unit time (功率) Φ = dQ/dt[w=watt][lm=lumen]
Radiant Intensity,单位立体角上的功率
(立体角是二维弧度制角的三维延伸,在二维里面,我们用弧长和半径的比来描述角度,随着半径增加,弧长成比例增加,这个比例不变,满足描述角度的需求。三维里,从球心向物体看过去,与单位球相交出一块面积,面积和半径平方的比定义立体角,本质上就是一个锥角。使用θ(垂直角),Φ(水平角)描述方向向量,结合半径r可以确定空间任意一点的位置,单位球的半径是1。)
球面上单位面积即为:dA=(rdθ)(rsinθdΦ)
单位立体角即为:dω= dA/(rr)=sinθdθdΦ
在单位立体角,单位投影面积上的功率。
ncident Radiance(入射),就是说Irradiance是会向各个方向辐射进入的,但度量某一方向的irradiance就成了Radiance,可以看出来Radiance比Irradiance多了方向性。
Exiting Radiance(出射),就是单位投影面积发出的intensity。
dA面积收到的来自所有方面的能量即为Irradiance,而某一个方向上入射的能量就是Radiance,所以Irradiance和Radiance本质的区别就是方向性,对所有方向上的Radiance积分就可以得到Irradiance。
四、光线传播和全局光照
一束光打在某一平面上,平面吸收了这束光的能量。然后这个平面作为一个能量源向各个方向辐射能量,使用BRDF(Bidirectional Reflectance Distribution Function),双向反射分布方程来描述能量分布。
对每个方向发出的能量积分,即可得到该平面吸收的能量。如果该平面不仅仅反射光,同时还是光源,需要添加该部分能量。
L=E+KL:L,E都是向量。K是表示对内核的积分的运算符
- E,常数项,自身光源发出的光
- KE,一次项,光源直接照射着色点,直接光照,光发生一次弹射
- K2E,二次项,间接光照,光发生两次次弹射
全局光照,就是直接光照和间接光照的集合,而光栅化里的着色,就是只做了直接光照和光源自己,即E+KE。而全局光照中引入的高次项越多,即越逼近真实场景,但高次项的作用不是线性的,随着次数增大,后面的改善就越发趋于饱和。
Monte Carlo Integration蒙特卡洛积分
渲染方程是满足真实物理规律的,可以描述光线的传播,但是渲染方程中有一个积分项,这个积分函数很难通过直接写出数学解析式进而求解积分。
蒙特卡洛方法可以通过随机采样的方式求解数学问题,对于求解定积分问题,可以通过蒙特卡洛方法估计出一个近似数值解。我们把积分变量看成连续型随机变量,每次采样,就用采样所得变量映射得到的函数值,代表所有积分区间内所有变量对应的函数值,多次采样逐渐逼近真实函数的在积分域内所得积分值。
在求解积分过程中,我们只需要知道变量对应的函数值和概率密度分布即可。使用函数值除以概率密度,相当于用单次使用样本来估计总体的过程,然后多次估计求平均即可近似真实值。
蒙特卡洛积分是一种无偏估计,通过对积分式求数学期望,可以发现期望就是定积分值。
光线追踪
Whitted-style ray tracing。
- 光线打到一个物体,然后和光源连线。
- 遇到光滑的物体,发生反射和折射。
- 遇到漫反射的物体,光线停止。
光线遇到漫反射的物体,光线不应该停下来,所以漫反射材质的物体渲染效果不真实(glossy reflection),而且忽略了漫反射物体发出的光线,导致环境缺少其照射产生的效果(亮度和颜色)。
渲染方程有两个特征:
- 包含一个在半球面上的积分项
- 因为对光照来源不做区分(光源和反射光),求解方程是一个递归的过程
1.直接光照
2. 全局光照
全局光照就是直接光照加上间接光照
用n=1做蒙特卡洛积分,就叫做Path Tracing。
俄罗斯轮盘赌
就是设定一个光线继续弹射的概率,也就是通过概率的方式,让递归终止,在开始递归之前,先计算一个随机数,如果在概率范围内,继续迭代;否则return,光照结果是0。
最后
以上就是帅气冬瓜为你收集整理的GAMES101笔记(三)一、光线追踪二、加速结构三、辐射度量学四、光线传播和全局光照的全部内容,希望文章能够帮你解决GAMES101笔记(三)一、光线追踪二、加速结构三、辐射度量学四、光线传播和全局光照所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复