计算机图形学笔记——光线追踪(Ray Tracing)

6,055 阅读23分钟

前言

此系列是本人对于学习计算机图形学过程中的记录及总结,所看教材是《Fundamentals of Computer Graphics (FOURTH EDITION)》。本文的图片也主要来源此书。以博客作为学习记录,理解之中若有不足还请大佬们指教。

本篇主要记录第四章Ray Tracing的学习。

真实世界中,我们之所以能看见物体是因为有光的存在。物体的表面反射部分光,使得人眼能够看见物体。比如红色颜料能够吸收蓝色光源和绿色光源,反射红色光源,所以红色颜料看起来是红色的。

通过假想的相机、模拟现实世界中的光线运动等等,光线追踪能够生成比ray castingscanline rendering更加逼真的场景。比如,光线追踪生成的阴影、反射效果等等更为真实,可以达到相片级的效果,这些是光线追踪的强项。拿图说话:

光线追踪是一个比较古老的算法了,第一个光线追踪算法由Arthur Appel在1968年提出,当时的光线追踪并不被称为“ray tracing”,而是被称为“ray casting”。此时的光线追踪像素值颜色仅由击中物体决定,而忽视了反射光、阴影等等。1979年是一个新的突破点,Turner Whitted继前人结果提出光在击中物体表面时可产生新的三种光线:reflection(反射)、refraction(折射)、 shadow(阴影)。

光线追踪一个严重的缺点即是性能问题。像扫描线渲染(Scanline rendering)及其他的渲染算法能使用数据一致性在像素间共享计算结果,而光线追踪算法通常需要分别重新计算每一个像素值。电影制作中,一台机器光线追踪渲染一帧即需要几十甚至几百小时。

因为其性能耗费的巨大,光线追踪传统使用为离线渲染,应用于动画、电影特效等等。但是目前实时光线追踪也正在发展,并寄希望于将其应用于游戏之上。在知乎上看见一个相关的讨论,回答覆盖从2014年——2019年,对比前后看实在令人感慨。链接:什么是实时光线追踪技术?它可能出现在当前的次世代主机上吗? - 知乎

总述

渲染3维物体是计算机图形学的一个基本任务:根据特定视角,将场景、3维空间中几何物体的组合生成2维图片。更根本地说,此渲染是将一系列对象作为输入,像素数组作为输出。

根据渲染时考虑物体对象如何输出为像素,有两种常规方法:

  • object-order rendering:以物体对象为顺序,遍历物体对象时会找到并更新所有被当前物体对象影响的像素
  • image-order rendering:以像素为顺序,遍历每一个像素时所有影响此像素的物体对象会被找到,并计算此像素值。

可以如此认为两种渲染的不同:

//object-order rendering
for each object:
    ...
    ...
 
//image-order rendering
for each pixel:
    ...
    ...
    

image-order rendering和object-order rendering方法均可计算出完全相同的图片,但是他们适合于计算不同的效果,同时有着不同的性能特特点。比较各自的优势会在第八章中讲述。

image-oreder rendering更加容易实现,更加灵活,通常也会花费更多执行时间去产生可以相比较的图像。

光线追踪是一种为渲染3维场景的image-order算法。

4.1基本的光线追踪算法(The Basic Ray-Tracing Algorithm)

光线追踪每次都会计算像素值,遍历每一个像素时,基本任务是寻找图像中位于像素位置的物体对象。任何一个被像素“看见”的物体对象一定与viewing ray相交。

viewing ray, a line that emanates from the viewpoint in the direction that pixel is looking.

在计算像素时,我们想要得到的物体正是viewing way穿过的、距离摄像机最近的物体,因为它阻挡了位于它之后的其他物体对象。当物体对象找到时,shading会根据相交点、曲面的法线和其他信息等,决定像素点的颜色。

In computer graphics, shading refers to the process of altering the color of an object/surface/polygon in the 3D scene, based on things like (but not limited to) the surface's angle to lights, its distance from lights, its angle to the camera and material properties (e.g. bidirectional reflectance distribution function) to create a photorealistic effect.

Shading is performed during the rendering process by a program called a shader.——Shading - Wikipedia

因此一个基础的光线追踪(ray tracer)有以下三部分:

  1. ray generation:基于相机,计算每个像素viewing way的原点和方向
  2. ray intersection:发现与viewing way最近相交的物体对象
  3. shading:基于ray intersection的结果计算像素颜色

基础的光线追踪程序如下

for each pixel do
    compute viewing ray
    find first object hit by ray and its surface normal n
    set pixel color to value computed from hit point, light, and n

本章节将会覆盖ray generation、ray intersection、shading的基本方法。

4.2透视(Perspective)

把3维物体或者场景绘制到2维平面上早在计算机前就一直被艺术家们所研究。尽管到目前为止有许许多多的方法,对于艺术、摄影、计算机图形来说,最为标准的方法是linear perspective(线透视)

投影有平行投影(parallel projection)透视投影(perspective projection)

最简单的是平行投影。平行投影中,直接把3维空间的点沿着投影方向移动,直到点“打在”平面上。

因为平行投影保持了物体的尺寸和形状,不会因为距离的远近而改变大小,所以平行投影经常用于机械和建筑构图。平行投影的优点也是他的缺点,我们眼睛看物体的感受是近大远小,而平行投影则不会。

如果要产生更自然的视角就要使用透视投影,也就是此章开头提到的透视。它可以产生近大远小的效果。

如上左图,通过*视角点(viewpoint)*投影射线,而不是使用平行线。使用这种方式,距离更远的物体被投影时就会自然变小。

4.3计算Viewing Rays(Computing Viewing Rays)

ray generation的基础是viewpoint(对平行投影来说是view direction)和图像平面(image plane)。有许多方法可以实现如此的相机几何。本节会阐述基于标准正交基的相机几何。

为了产生射线,我们需要一个对射线的数学表达。一条射线即为一个原点和一个方向。3维参数直线(3D parametric line)是一种理想的实现方式,从眼睛e到点s可用如下公式表示:

以上公式可以解释为:我们从e沿着向量(s-e),使用t控制距离,去找到点p。所以通过t,我们可以决定点p在哪里。e是射线的原点,s-e是射线的方向。

特别地,p(0)=ep(1)=s。同时,若0<t1<t2,则p(t1)比p(t2)更靠近眼睛e。如果t<0则点p会在眼睛位置后方。这些发现在我们后面判断射线击中最近物体、同时不在眼睛位置后方时特别有用。

4.6图中的平面也是映射图像的图形平面(image plane),如下(图自:What is Ray Tracing - YouTube,后文有更多image plane相关知识)

想要计算一条射线的话,需要es。直接寻找s可能比较困难,但是如果在右手坐标系中则会直观很多。

所有从正交坐标系体系发射射线的ray-generation均为称为camera frame。camera frame中定义e做为眼睛或者viewpoint,uvw作为三个基础向量。从相机视角,v指向上,u指向右,w指向后方。

{u, v, w}组成了一个右手坐标系。

如上图示,在Screen上放一些3维样例点,射线射向每一个点。

4.3.1正投影视图(Orthographic Views)

对于正投影视图来说,所有的射线均有一样的方向,即-w。尽管平行视角没有视角点,但可以在相机原点定义一个平面。我们需要在平面上定义图片的边界,如此便能知道图片将会映射在平面上的那些位置。主要定义图片的四个参数:l代表left,r代表right,t代表top,b代表bottom。通常l<0<r,b<0<t,如图(蓝色的平面即为图像平面,像素点均在此平面上)。

3.2章节中曾经讨论过图片中的像素坐标系,为了使nx * ny图像生成,匹配(r-l) * (t-b)的图像平面,图像平面中每个像素的水平间距为( r-l )/nx,垂直间距为( t-b )/ny。同时为了像素点坐标处于网格点,在左边边缘与下边边缘会增加半个像素值。这意味着光栅图中点(i, j)在图像平面上的位置为(u, v),如下:

在正投影视图中,使用像素在图像平面上的位置作为射线的起始点,同时射线方向为视角方向(此例中为-w)。最后产生正投影的viewing way如下:

公式(4.1)正是上面计算u、v的公式

4.3.2透视视图(Perspective Views)

对于透视视图来说,所有的射线都有同一个原点,即为viewpoint。同时每一个像素的射线方向都是不一样的。图像平面不再位于原点,而是距离ed距离,此距离被称为image plane distance(图像平面距离???),常常也被称为focal length(焦距),因为这个d距离扮演的角色和相机中的焦距是一样的。每一条射线的方向则由viewpoint及图像平面上的像素点决定,如图。

最后生成图像的过程也与正投影视图类似:

综合以上内容,从像素坐标系到viewing ray及与image plane的关系,我绘制了一幅图以便理解

4.4射线-物体相交(Ray-Object Intersection)

以上我们已经学会光线追踪的ray generation部分,知道怎么发射viewing way。接下来便是光线追踪的ray intersection部分。

一旦我们产生射线e+tdd即为射线方向),接下来便要寻找第一个与射线相交的物体,同时t>0。实践中,发现第一个相交物表面的t范围在[t0, t1],其中t0=0,t1=+∞。我们先用此公式解决球面三角形的问题,在下一章节中再讨论更多的几何物体。

4.4.1射线-球面相交(Ray-Sphere Intersection)

给定射线p(t)=e+td与隐式曲面f(p)=0,我们想要知道它们的相交点。相交发生时,相交点会同时满足两个方程:即在射线上与曲面上。所以有以下等式:

中心为c,半径为R的球面方程可用如下隐式方程表示:

用向量表示的话可变为以下写法:

如此。带入上式我们即有:

按照(td+(e-c))展开即有:

如此便等价于我们熟悉的一元二次方程形式:

从中可获得判别式B^2-4AC

  • 若判别式<0,则射线与球面未相交
  • 若判别式>0,则射线与球面有两个相交点,一个是进入球面的点,一个是离开球面的点
  • 若判别式=0,则射线与球面相切,只有一个相交点,此相交点称为切点

进一步运用解一次二元方程的知识,可以获得如下等式:

4.4.2射线-三角形相交(Ray-Triangle Intersection)

目前有许多计算射线-三角形相交的算法,本节会展示*重心坐标(barycentric coordinates)*下包含三角形的参数化平面,这种算法不同于顶点组成的三角形,它不需要长期的存储空间。

在重心坐标中,若三角形的三个顶点为abc,当满足以下等式时相交就会发生:

如果β>0,γ>0,同时β+γ<1,相交就会发生在平面的三角形内,否则的话相交就发生在平面上的非三角形区域内。如果无解的话,则为三点未形成三角形(比如在同一条直线上)或者射线与平面平行。

为了得到t、β、γ,可以将以上等式展开:

以线性方程组的形式重写:

最后求解:

其中,|A|为:

如此,对于射线与三角形是否相交的函数算法如下:

4.4.3 射线-多边形相交(Ray-Polygon Intersection)

对于多边形的方法是可以把点p和多边形顶点投射到二维平面上进行判断,此部分在此不做赘述。而实践中对于多边形与射线相交的处理经常是把一个多边形划分为多个三角形。

4.4.4与一组物体对象相交(Intersecting a Group of Objects)

多数场景不单单只有一个物体对象。当发出射线时,我们需要沿着射线寻找距离相机最近的物体对象。

对于多物体对象,有一个简单的方法是把一组物体对象作为整体,视作另一类型的对象。为了使射线和组相交,可以在组内发射射线与物体对象相交,同时返回相交时最小的t。写作算法如下:



至此已经完成光线追踪的ray intersection部分,用于发现与viewing way最近相交的物体对象。如果不做进一步shading的处理,此时渲染出来的每一个像素仅仅是与射线相交物体固定的颜色值。

4.5浓淡处理(Shading)

In computer graphics, shading refers to the process of altering the color of an object/surface/polygon in the 3D scene, based on things like (but not limited to) the surface's angle to lights, its distance from lights, its angle to the camera and material properties (e.g. bidirectional reflectance distribution function) to create a photorealistic effect.
Shading is performed during the rendering process by a program called a shader.
——Shading - Wikipedia

至此是光线追踪的最后一部分shading。在上一步的ray intersection中,一旦像素知道了其对应的物体表面,通过shading model能计算其相应的像素值。

本节讲述两个最基本的shading model,更先进的shading model在第十章。

多数的shading model,都是为捕获光的反射过程而设计的。这些光主要来自物体表面反射光源和反射向摄像机的光。

简单的shading model是在一个点光源的基础上定义的。在光的反射过程中,有以下重要的变量:

  • light direction l:指向光源的单位向量
  • view direction v:指向摄像机(或眼睛)的单位向量
  • surface normal n:反射发生时,垂直于反射表面的单位向量
  • 其他:surface color、surface shininess、其他细节等等

4.5.1Lambertian Shading

最简单的shading model基于Lambert于18世纪的观察结果:一定面积区域内,照射在此区域下,从光源得到的能量依赖于光与表面的夹角。光源垂直射向表面,表面反射最多;光源未射向表面,表面无反射。此区间下,反射强度与表面法向量n和光照l夹角θ的cosθ值成比例。

这便是Lambertian shading model

变量解释:

  • L:像素颜色
  • kd:散射系数(diffuse coefficient)或者表面颜色
  • I:光源强度——光强
  • n · l:因为nl均为单位向量,所以cosθ值可用n · l更方便地表示及计算

这条公式分别应用于三个颜色通道(RGB),因此计算R值时,使用的是red diffuse coefficient、红光强度(red light source intensity)和点乘。对G值和B值类似。

l向量是通过viewing way与物体对象表面相交点与光源点位置相减得到的。牢记vln*三个向量必须为单位向量。未将向量规范化(normalize)是许多shading计算错误的原因

4.5.2Blinn-Phong Shading

Lambertian shading是一种与视角无关(view independent)的shading:材质表面的颜色不会受不同观看角度而影响。举些例子,许多更逼真的表面会有highlights或者specular reflection效果,不同的viewpoint会带来不同的反射效果。

(根据上下文,此书中specular reflection指高亮、反光效果,mirror reflection指镜子一样的镜面反射,mirror reflection是理想情况下的specular reflection)

Specular reflection, also known as regular reflection, is the mirror-like reflection of waves, such as light, from a surface. In this process, each incident ray is reflected at the same angle to the surface normal as the incident ray, but on the opposing side of the surface normal in the plane formed by incident and reflected rays. ——Specular reflection - Wikipedia

Lambertian shading无法产生hightlights效果导致了表面的不平滑。许多shading model在Lambertian shading的基础上添加了反射成分(specular component),而原本的Lambertian shading部分则作为散射成分(diffuse component)

Blinn-Phong Shading一个非常简单并被广泛适用于反射高亮的模型,由Bui Tuong Phong所提出,并在后来被J.F.Blinn所改进至如今通用的样子。

Blinn-Phong shading的原理是:当vl对称于表面法向量时,发生的反射是镜面反射(mirror reflection)。Blinn-Phong shading设定在此时产生的反射最为高亮,随着vl的位置关系逐渐偏离镜面反射的条件,反射的强度平滑下降。

接下来是具体流程。首先,设置vl的half vector h(即vl的中间向量,与两向量夹角相等),通过hn的夹角判断偏离镜面反射条件的程度。如果hn的夹角很小,此时反射成分应该很亮;如果hn的夹角很大,此时反射成分应该较暗。夹角大小的判断同样可以通过n·h(即点积)来判断(注意nh均为单位向量)。得到点积结果后,对其进行p次幂运算(p>1),使得亮度下降更快。

以上变量中,p被称为power或者Phone exponent,用于控制表面的闪光程度,p有如下代表值:

h则也易于计算:将vl两向量相加后规范化即可。

综上整个流程的示意图如下:

模型如下:

其中ks代表specular coefficient(反射系数),或者表面的反射颜色。

公式中也可看出其基于Lambertian shading的改进。

4.5.3Ambient Shading

没有收到光照的表面会被完全渲染为黑色。

在真实世界中,未被光源直接照射的表面会被其他表面发射的光所照射。

为了去除黑色的阴影,一个简单暴力但有效的方式是在shading model中添加一个常量成分(constant component)。计算像素值时,此常量成分的值仅仅依赖于射线击中的物体对象,完全与几何表面不相关。这便是ambient shading,在此模型下,物体表面都会呈现出被环绕在周围(ambient)的光照射的效果。

为了方便参数化,ambient shading经常用物体表面颜色(surface color)与环境光颜色(ambient light color)的点积表示,如此ambient shading可应用于个别不同的表面上或者所有的表面上。

在Blinn-Phong Shading的基础上,ambient shading即为一个简单有用的shading model:

其中,ka是surface's ambient coefficient(。。实在不知道咋说成中文了)或者“ambient color”。Ia是环境光强度。

4.5.4多个点光源(Multiple Point Lights)

光一个非常有用的性质为可叠加性(superposition)——当有多个光源时,只需要把各个光源的效果相加。因此,我们之前简单的shading model可以很容易地做如下扩展至N个光源:

其中,Iilihi分别表示第i个光源的强度、方向、和half vector。

4.6一个光线追踪程序(A Ray-Tracing Program)

至此,我们已经知道了给定像素如何发射viewing way,如何找到最近的相交物体,如何根据相交的结果进行shade。,这三个部分即对应ray generationray intersectionshading,即为光线追踪程序的基本部分。整体框架如下:

在实践中,surface intersection环节需要返回击中物体的引用,或者其法向量与shading相关的材质特性。在以物体对象为导向的执行过程(object-oriented implementation)中,有一个好方法是使用由surface的类派生的triangle,sphere,group类等等。所有与射线相交的表面都会在surface类下,同时各种新的对象及高效的组织结构也可以透明地附加上去。

4.6.1Object-Oriented Design for a Ray-Tracing Program

如前所述,光线追踪中的关键类层次(key class hierarchy)是构成模型的几何对象。但是这些关键类本应该是一些几何对象类的子类,同时它们应该有hit函数。为了避免与"object"混淆,这些关键类的类名常用surface

In technical applications of 3D computer graphics (CAx) such as computer-aided design and computer-aided manufacturing, surfaces are one way of representing objects. The other ways are wireframe (lines and curves) and solids. Point clouds are also sometimes used as temporary ways to represent an object, with the goal of using the points to create one or more of the three permanent representations. —— Computer representation of surfaces - Wikipedia

其中,一个重要的点是任何能被射线“击中”的物体都应该是类层次的一部分。举个例子,甚至是surface的集合都应该被认为是surface的子类,包括一些高效的结构,比如说bounding volume hierarchy - Wikipedia

接下来对surface类举个例子,基础的surface类会声明hit函数和bounding box函数:

其中(t0, t1)是射线可能击中物体对象的间距范围,rec是通过引用,传递击中信息的记录。rec包含的数据比如有相交发生时的t值。

第二杆函数 bounding-box返回的box数据类型是3维的“bounding box”(在Wikipedia中查到的是Bounding volume),用两个点定义一个轴对齐的、围起封面的盒子,如下图:

如何计算这个“bounding box”呢?拿球面来举例如下:

另外一个有用的类是材质(material)。把材质作为类可以帮助我们把材质的特质行为抽象出来,同时可以透明地为物体对象添加材质。一个把物体对象同材质连接起来的简单方法,是在surface class上添加连接材质的点。需要解决的问题即是如何对纹理(texture)进行处理,纹理(texture)是否为材质类(material class)的一部分,还是独立于材质类?这些会在此书的第十一章中讲述。

4.7阴影(Shadows)

在基本的光线追踪程序完成后,想要阴影并非难事。

假设我们看到点p,在点p位置沿着l方向“看去”,如果有物体遮挡,则点p在阴影之中;无物体遮挡,则点p不在阴影之中,即光源未被遮挡。

如上,q+tl击中物体对象,q在阴影中;p+tl未击中物体对象,p不在阴影中。因为场景中光源距离较远,所以对所有点来说l向量是一样的。为了与viewing rays区分,q+tlp+tl此类射线称为shadow rays,用于判断点是否处于阴影之中。

我们可以在之前的算法上加上对shadow的判断。对于shadow view,理论上来说t的取值范围可以为[0, ∞),但是会由于数值的不精确导致使相交检测直接发生于p点所在的表面(surface)。为了避免这种情况,通常的做法是使t的范围为[ε , ∞),其中ε是一个极小的正数常量。

在此前的ambient shading model上进行改进,可得算法:

4.8理想的Specular Reflection(Ideal Specular Reflection)

理想的specular reflection即mirror reflection,意味着看像镜面一样的反射。反射内容的观察角度可以如下图:

从右处的眼睛看理想的镜面给,相当于从镜面下方以相同的θ角沿r向量方向看去。对应r向量可以如下计算:

在真实世界中,在表面发生光线反射时,一些能量会损耗,对于不同颜色的表面,损耗是不同的。比如金色表面反射黄光比反射蓝光更加高效,所以显现出来的颜色为其反射更为有效的颜色。要达到此效果,在color c加上对raycolor的递归调用:

其中km是对应反射面的RGB值(specular RGB color)。这个递归函数未设置终止条件,可以通过限定递归深度来改良。要使代码效率更高效,可以继续添加判断条件:仅仅当km值非0时(0即为黑色),反射光线才会发射。

4.9Historical Notes

在计算机图形学的发展史上,光线追踪很早就被提出(Appel,1968),但是直到计算机的计算能力足够强大才被经常使用(Kay & Greenberg, 1979; Whitted, 1980)。

光线追踪有着比object-order rendering更低的渐进时间复杂度(Snyder & Barr, 1987; Muuss, 1995; S. Parker et al., 1999; Wald, Slusallek, Benthin, & Wagner, 2001)。

尽管光线追踪传统上使用一般是离线渲染,但是目前实时光线追踪正在发展。(这部分书中未具体提及,关于光线追踪的应用发展在知乎上看见一个2014年很好的回答——什么是实时光线追踪技术?它可能出现在当前的次世代主机上吗? - 豆花鱼的回答 - 知乎 把2014的回答看至2019的回答实在有趣)

总结

其实本文本来就是总结,如下写个总结的总结:

看完此章节后对光线追踪有了更加深刻地理解,而不是之前单纯的遍历像素,发射viewing way,计算像素值。对其中遇到的问题及解决、代码设计等收获了很多。

上述内容可以基本概括为下图及流程:

viewing ray——>ray intersection——>shading

参考资料

Shading - Wikipedia
What is Ray Tracing - YouTube
Blinn–Phong reflection model - Wikipedia
Specular reflection - Wikipedia
Bounding volume hierarchy - Wikipedia
Bounding volume - Wikipedia
Computer representation of surfaces - Wikipedia
什么是实时光线追踪技术?它可能出现在当前的次世代主机上吗? - 豆花鱼的回答 - 知乎
色彩——百度百科
光线追踪是什么?哪个显卡支持光线追踪? - NVIDIA英伟达的回答 - 知乎
游戏画面是实时渲染的吗? - 罗必成的回答 - 知乎
请问光栅化与渲染这两个术语的区别和联系是什么? - Milo Yip的回答 - 知乎