Monte-Carlo Ray Tracing System (一)原理以及架构设计

实时全局光照

对于现在的CG相关行业来说 就如同一个待跨越的圣杯一样。而在GPU不断进步的过程中,我们却对实现全局光照越来越没有信心 。性能不够也是我们最常在嘴上提到的词语
但是可以说其核心原理早就实现了 在这些年其核心原理并没有取得很大的突破这也是现阶段无法实现实时全局光照的原因之一。


对待这样一个系统来说 我们要先从原理说起也就是Monte-Carlo 数值积分。因此第一篇文章着重于算法和设计 具体的代码将不会很多。

Chapter (1)

10x2dx=13

说起
在通常的过程中 对于这样一个简单的定积分我们人脑的求解过程是以固定的微分积分公式计算,公式虽然能方便计算出定积分的精确值,但是有一个局限就是要首先通过不定积分得到被积函数的原函数。有的时候,求原函数是非常困难的,而有的函数,如

f(x)=sinxx

,已经被证明不存在初等原函数,这样,就无法用Newton-Leibniz公式,只能另想办法。

我们以 y<(x2) 作为判断依据去划分区域来判断区域划分 再通过随机生成的点去重复的做判断

当随机点数量到达一定之后 我们以两个区域的点的数量作为依据 即可得出结果 且点数越多误差越小
根据伯努利大数法则:事件发生的频率依概率收敛于事件的概率p

如下图:
Monte

而一般来讲Monte Carlo方法虽然可以解决很多疑难杂症 但是对于复杂度要求极高的光线追踪领域 其结果十分惊艳 但并不能够称之为一个十分简洁的算法

下面我们进入下一章


我们在其他文章中讲到了光照模型这一概念 然而应用在传统的实时渲染领域 光照模型则是一个相对高效但是低质的概念 所在实时渲染领域 其多数是考虑怎么尽量的使用障眼法去得到一个更逼近数值积分方法的结果 在洪培技术预计算的技术发展方面

类似于UE4的静态效果 Paris demo 在美术和实时光照技术的双重作用下 其效果已经达到了现有计算条件下画质的巅峰
这里写图片描述

但是相比真正的全局光照,效果仍旧差了一些。

也许会有人说 如此大的代价 去提升那20-30%的画质 值不值得 ?

当然值得!!

在讲解下一章之前我们来了解一下传统的光栅化渲染技术


什么是光栅化?什么是光栅?

光栅是光学中一种常见的概念 意为大量等距平行狭缝
然而在实际中光栅化的意义接近于像素化,离散化;用已知概念去理解 类似于透过纱窗去看外面的世界
传统构成三维观察方法常用的为等轴测投影和透视投影。

首先要讨论的是如何把一个三维模型的数据集绘制成相应的模型

这个阶段其实在《Real time Rending》这本书和之前的文章里介绍的很清楚了

渲染管线的基础结构分为:
这里写图片描述

1.Application应用阶段

需要渲染的几何体从一个固定的数据结构被传递到几何阶段。这传递过去的东西被称为图元,例如点、线、三角形,这些图元有可能最终显示到屏幕上。这是应用阶段最重要的任务。
由软件实现的应用阶段的问题可能在于,它不能像几何和光栅化阶段那样划分为多个子阶段。但是,为了提高性能,这一阶段经常是由多核并行执行。在设计中,这称之为超标量体系,

2.Geometry几何阶段

这里写图片描述
在这个阶段一般是对模型进行视图变换 对操作进行响应 对顶点进行着色 投影 裁剪和屏幕映射

相应的例如变换过程如下

这里写图片描述

裁剪过程
这里写图片描述

裁剪之后的图像 还不能用来显示 现代操作系统的图像大多数运行在窗口之下 所以针对已经计算好的显示方式来说还需要最后一步 那就是图像帧对屏幕相应区域的映射

过程如下图:

这里写图片描述

从另一个角度我们也可以考虑到 为什么全屏运行的游戏 效果会更好一些 这与省略了映射阶段也有着一定的关系

3.Rasterizer光栅器

在这个阶段的细分任务中我们算是可以看出这些效果的成因
这里写图片描述

渲染的过程大致相当于:

这里写图片描述

至于牵扯到具体的光效 ,阴影 等信息着色器就开始担当大任啦
具体可见:着色器部分

讲完了光栅化体系 我们可以看得到 其效果都是由理论模型模拟效果 效率在现在的光栅化芯片加持下 还算不错 在好的硬件中如GTX1080Ti 甚至能把一个4K 的普遍场景渲染到120Hz

但是

在大多数作品中 我们不可能达到非常好的效果 因为即使类似Unreal Paris demo这种 预渲染的效果 已经计算好了光照 美术和设计还有着色器方面的设计要求实在是太高了

那么 我们实现实时全局光照的意义在哪里?

举个例子 如果在一个场景之下 我们对一个像素需要30条光线来收敛 以最小的720p 也就是1280*720分辨率 再加上60hz的刷新率 可以看到每秒钟需要处理的光线数量达到了16亿5000万条

那我们以一个正常效果的收敛来计算
收敛采样数为200 1080p分辨率 60hz刷新率
每秒钟需要处理的光线数量达到了248亿

不得不承认

在传统的SIMD GPU架构上光线追踪的效率被大大折扣 因此移动GPU巨头 多年为苹果设计GPU的Imagination甚至收购了一些企业 制作了光线追踪加速卡 以至于我们可以在移动端的功耗前提下实现稳定的光线追踪技术
以下为相应的架构图

这里写图片描述

而我们再看看SIMD

这里写图片描述

因此实时光线追踪绝不是遥不可及的技术


但是仅仅这些我们不足以对光线追踪产生如此大的兴趣 因此实时光线追踪的好处甚至包含了准确反映光的衍射,色散,等等光学特性 这在传统体系之下几乎是无法做到的

最终结合计算物理中的流体模拟,动力学模拟,甚至量子物理模拟 在计算机中建立一个完全拟真的世界 永远是人类为之努力的目标

很庆幸的是 这其中很多技术都取得了关键突破

Chapter(2)

1979年,TurnerWhitted在光线投射的基础上,加入光与物体表面的交互,是光线在物体表面沿着反射,折射以及散射方式上继续传播,直到与光源相交这一方法后来也被称为经典光线跟踪方法、递归式光线追踪(Recursive Ray Tracing)方法,或 Whitted-style 光线跟踪方法。

其主要思想是从视点向成像平面上的像素发射光线,找到与该光线相交的最近物体的交点,如果该点处的表面是散射面,则计算光源直接照射该点产生的颜色;如果该点处表面是镜面或折射面,则继续向反射或折射方向跟踪另一条光线,如此递归下去,直到光线逃逸出场景或达到设定的最大递归深度。
by浅墨

原理:

我们如果能看到一个物体的某个点 这个点必然反射/折射了光线

而对于光线追踪这个过程本身来讲就像是自然世界的逆过程,朝着我们能看到的所有点发射光线,追踪次光线(shadow ,reflection ,refraction)。必然能够回到光源。

这里写图片描述

图中的几个部分分别说明了 对象的表示 光线的求解方式
相交的求解方式 等等 我们分公式来举例说明一下每个公式怎样用代码求解

基础定义类

三位向量运算类
template<typename T>
class Vec3
{
public:
    T x, y, z;
    Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
    Vec3(T xx) : x(xx), y(xx), z(xx) {}
    Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
    //正规化
    Vec3& normalize()
    {
        T nor2 = length2();
        if (nor2 > 0) {
            T invNor = 1 / sqrt(nor2);
            x *= invNor, y *= invNor, z *= invNor;
        }
        return *this;
    }
    //运算定义
    Vec3<T> operator * (const T &f) const { return Vec3<T>(x * f, y * f, z * f); }
    //标量*向量
    Vec3<T> operator * (const Vec3<T> &v) const { return Vec3<T>(x * v.x, y * v.y, z * v.z); }
    //向量1*向量2
    T dot(const Vec3<T> &v) const { return x * v.x + y * v.y + z * v.z; }
    //点积
    Vec3<T> operator - (const Vec3<T> &v) const { return Vec3<T>(x - v.x, y - v.y, z - v.z); }
    //向量1-向量2
    Vec3<T> operator + (const Vec3<T> &v) const { return Vec3<T>(x + v.x, y + v.y, z + v.z); }
    //向量1+向量2
    Vec3<T>& operator += (const Vec3<T> &v) { x += v.x, y += v.y, z += v.z; return *this; }
    //向量自增
    Vec3<T>& operator *= (const Vec3<T> &v) { x *= v.x, y *= v.y, z *= v.z; return *this; }
    //向量自乘
    Vec3<T> operator - () const { return Vec3<T>(-x, -y, -z); }
    //求负
    T length2() const { return x * x + y * y + z * z; }
    //模^2
    T length() const { return sqrt(length2()); }
    //模
    friend std::ostream & operator << (std::ostream &os, const Vec3<T> &v)
    {
        os << "[" << v.x << " " << v.y << " " << v.z << "]";
        return os;
    }
};

当然 这是较为精简的写法和部分运算 下篇文章中 我们将着重来尝试优化渲染效率和效果 但是其中涉及的复杂运算不利于基础概念的理解 所以我们用比较基础的运算来说明

同时按照图中顺序 我们将对这整个过程作一个说明


Sphere / Ray intersection (给出光线和球的表达式 求相交)

Sphere equation/三维向量的球面表达式 可以想象球面点到球心的差的平方为半径的平方

(p⃗ c⃗ )(p⃗ c⃗ )=r2


    Vec3f center;                           /// position of the sphere
    float radius, radius2;                  /// sphere radius and radius^2
    Vec3f surfaceColor, emissionColor;      /// surface color and emission (light)
    float transparency, reflection;         /// surface transparency and reflectivity
    Sphere(
        const Vec3f &c,
        const float &r,
        const Vec3f &sc,
        const float &refl = 0,
        const float &transp = 0,
        const Vec3f &ec = 0) :
        center(c), radius(r), radius2(r * r), surfaceColor(sc), emissionColor(ec),
        transparency(transp), reflection(refl)
    { /* empty */ }

Ray equation

r⃗ (t)=o⃗ +t
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值