计算机图形学之光线跟踪算法的研究与实现2017年我的优秀毕业论文

计算机图形学之光线跟踪算法的研究与实现2017年我的优秀毕业论文

版权所有使用者请联系我 刘创 QQ:903188593

2.2.2 Phong光照模型

事实上对于漫反射的物体表面,使用Lambert就足够,但是实际生活中并不存在这种理想的漫反射材质。Phong光照模型是现代真实图形学中提出的第一个有影响的光照明模型,不过该模型只考虑到了物体对直接光照的反射作用。此外,一般认为环境光系数因子是常量,没有考虑物体之间相互的反射光因素,物体间的反射光只用环境光表示。Phong光照模型是一种属于比较简单光照模型。

简单光照模型中仅考虑光源直接照射在景物表面所产生的光照效果,且具有一定的均匀反射率。由此而来,虽然在计算处理上变得简单,但是其缺点也显而易见:虽然不同的物体具有不同的亮度,但是同一物体表面的亮度被看成是一个恒定的值,没有明暗的过渡,导致真实感效果不强。在简单光照模型中会出现有的物体不透明,那么该物体表面呈现的颜色仅由其反射光决定。反射光由两部分组成,一是环境反射,二是漫反射与镜面反射。环境反射一般设入射光均匀地从周围环境入射至景物表面并等量地向各个方向反射出去,而漫反射分量和镜面反射分量则表示特定的光源照射在景物表面上后产生的反射光。

Phong光照模型是一种典型的简单光照模型。Phong模型的计算公式2.3如下:

I = + ∑(cosi + )         2.3

其中,为环境反射系数,为漫反射系数,为镜面反射系数,对场景中的所有特定光源进行求和,并有+=1。由式2.3看出,一旦反射光中三种分量的颜色以及它们的系数确定以后,从景物表面上某点达到观察者的反射光颜色就仅仅和光源入射角和观察者的视角有关。因此可以说,Phong光照模型实际上是纯几何模型。

Phong模型计算中涉及的各方向向量如下图2.3

 

 

 

 

2.3 Phong模型

3 光线跟踪UML设计实现

一个较好的程序不是代码量,而是这个程序把物体抽象的是否合适具体,是否有可扩展性,是否有重构,代码设计的是否有可重用性等等。本次程序设计中将首先采用UML23][4这个强大的面向对象程序设计工具来设计出相关的类图。

世界万物一切皆为物质,将这些物体模型均抽象为一个物体Object类,比如三角形、四边形、圆锥、圆柱、球体、组合体。那这些物体都有什么属性?它们都有材质属性,自发光系数、漫反射系数、反射系数,有的物体也许会有折射系数,那么把这个材质抽象出来形成材质类Material,作为这些物体的父类,此时用到了C++中的多重继承,那么我们把这些抽象的物体一个一个的放进场景去,这个场景是什么?我们将场景抽象出来形成Scene类,Scene类中包含了这些物体。我们知道这个程序中算法发的核心之处:射线与场景中的模型求交。求交就要有射线,我们把射线抽象出来形成Ray类,求交一定会有一个结果,这个结果将会包含有交点的时间t、交点的位置position、法线normal、交点的材质信息或物体信息,我们将这些结果抽象出来形成交点结果类IntersectResult。光线跟踪相关类的设计详细信息如表3.1

3.1 光线跟踪相关类的设计详细信息

序号

类属性

函数

备注

1. 

Ray

Origion:原点

Direction:方向

Ray(Origion,Direction)

构造光线函数等

光线跟踪中的光线类

2. 

Vector3

X:三维坐标系下的x分量

Y: 三维坐标系下的y分量

Z: 三维坐标系下的z分量

构造三维向量的若干函数等

三维向量类

3. 

FLOATRGB

R:颜色中的红色分量

G:颜色中的绿色分量

B:颜色中的蓝色分量

构造像素点颜色的函数等

像素点的颜色类

 

 

续表3.1

序号

类属性

函数

备注

4. 

Material

Emission:自发光系数

Diffuse:漫反射系数

Specular:高光系数

Reflection:反射系数

构造材质函数等

材质类

5. 

MaterialUV

U:纹理u

V:纹理v

构造含有纹理映射的材质函数等

纹理材质类

6. 

TextureDataBmp

bmpDatabmp纹理数据读取存储二维数组

构造纹理信息函数等

纹理数据类

7. 

Objects

Material:物体的材质属性

bIsLight:物体本身是否为光源

构造场景中的物体函数等

场景中的物体集合类

8. 

Scene

Objects:场景中的所有物体

构造场景的若干函数

场景类

9. 

IntersectResult

Objects:相交的物体元素

T:相交的时间

Position:相交位置

Normal:相交点的法线

 

光线与物体求交后的结果类

10. 

RayTrace

Scene:光线递归所涉及到的所有场景

构造光线递归总函数等

光线递归类

11. 

Sphere

Center:球体中心

Radius:球体半径

Material:球体材质

构造球体、求交等函数

球体类

12. 

Triangle

V1,V2,V3:三角形三个顶点

Material:三角形材质

构造三角形、求交等函数

三角形类

 

续表3.1

序号

类属性

函数

备注

13. 

Plane

Normal:平面法线

D:平面距离

构造平面、求交等函数

平面类

14. 

FloorPlane

Normal:平面法线

D:平面距离

构造平面、求交等函数

特殊平面类

15. 

Cylinder

Center:柱体中心

Radius:柱体底面半径

Height:柱体高度

Material:柱体材质

构造柱体、求交等函数

柱体类

16. 

Cone

Center:锥体中心

Radius:锥体底面半径

Height:锥体高度

Material:椎体材质

构造锥体、求交等函数

锥体类

17. 

Compound

Center:组合体中心

Length:组合体长度

Width:组合体宽度

Height:组合体高度

Material:组合体材质

构造组合体、求交等函数

组合体类

 

 

 

 

 

 

 

4 向量工具类构

4.1向量类构造

本次图形学程序设计中涉及到很多的向量操作,比如球体的位置、射线的端点位置、射线的方向,反射、折射等的向量5,一个好的向量类设计将会为我们的程序工作带来很大的好处。向量vector中将会含有最基础的信息——欧式坐标(xyz),还有一些简单的操作符重载,例如两个向量的和、差、点击dot、叉积cross等。具体定义以及相关重要函数的定义部分代码:

class Vector3  

{

public:

float x;

float y;

float z;

public:

Vector3();

virtual ~Vector3();

Vector3(float x, float y, float z);

Vector3(const Vector3& v );

inline float Length2();

inline float Length();

void Normalize();

Vector3 operator + (const Vector3& v)const;

Vector3 operator -(const Vector3& v)const;

Vector3 operator - ()const;

Vector3 operator * (const Vector3& v)const;

Vector3 operator += (const Vector3& v)const;

Vector3 operator -=(const Vector3& v)const;

Vector3 operator *= (const Vector3& v)const;

Vector3 operator * (const float scale)const;

Vector3 operator * (const double scale)const;

};

inline float DotProduct(const Vector3& v1, const Vector3& v2)

{

return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);

}

 

inline Vector3 CrossProduct(const Vector3& v1, const Vector3& v2)

{

return Vector3(v1.y * v2.z - v1.z * v2.y,

v1.z * v2.x - v1.x * v2.z,

v1.x * v2.y - v1.y * v2.x

);

}

 

inline Vector3 operator*(float a, const Vector3& v)

{

return Vector3(a * v.x,  a * v.y,  a * v.z);

}

4.2射线类构造

数学中有关射线的定义:射线(ray)是指由线段的一端无限延长所形成的直线,射线有且仅有一个端点,无法测量长度(因为它无限长)。欧几里德几何学中定义为直线上的一点和它一旁的部分所组成的图形称为射线。几何学中的射线,我们通常形象地把它看作是手电筒发出的光线。

特点:只有一个端点和方向

参数方程:P = + t * u,其中是射线向量的起点,u是射线的方向,参数t的值范围(0∞)

u均是Vector3向量类的实例 ,向量表示如下图4.1所示

 

 

 

 

4.1 射线

射线方程:Ray(x, y, z) = Origion(x, y, z) + t * Direction(x, y, z)         4.1

其中,RayOrigionDirection均为Vector3向量类的实例。

4.3摄像机类构造

我们有一双眼睛所以通过通过我们的眼睛就可以去观察这个纷繁复杂的世界其实三维模型物体或者说图元渲染到二维屏幕的道理就如我们的眼睛去观察世界中的一草一木一样。首先我们将眼睛抽象具体化出来一个摄像机如下图3.4。这个摄像机就好比我们的眼睛,去看场景世界中的物体,此时就我们的眼睛的位置出发射向屏幕中的每个像素点,这将构成诸多的射线向量。我们再将这些射线向量进行与场景中的众多物体模型进行求交计算其中这些计算公式将在下一章节中详细介绍求交后我们找到了此射线向量最先交到的物体,然后通过获得的交点信息再去进行Phong模型光照模型进行渲染、反射、折射等计算,最终将所有计算的结果值完整的映射到屏幕上每个像素点所对应的RGB值,构成一幅逼真的图像最后由我们的电脑屏幕显示出来。

 

 

 

 

 

 

 

 

 

 

 

 

 

    4.2 场景观察中的摄像机

4.4反射向量推导、构造

在计算机图形学中,当我们计算光照模型时,经常需要求反射向量。一般的图形硬件函数库都会提供计算反射向量的方法,下面介绍一下我们如何自己计算反射向量。已知给定的入射光线向量I和平面法向量N,求反射向量R,如下图4.34.4。为了方便计算,我们这里假定IN都是单位向量(模为1,编程时可先将IN单位化)。

(1) 反射向量求解方法一:

 

 

 

 

 

 

 

 

 

4.3反射求解

设入射光线向量I和反射平面的法向量N之间的夹角为α。连接I的始端和R的末端,则有

R = 2P - I                           4.2

现在问题变成了如何求取P,设入射点0PN的交点的向量为S,那么有

P = I + S                                     4.3

那么该如何求取向量S?其实向量S即向量-N(注意,这里是-N,因为SN的方向是相反的)在向量N上的投影,根据高中向量的投影公式知识有

S = - N      4.4

因为N是单位向量,简化一下将得到公式

S = - (I·N)N      4.5

S代入公式4.4,再将P代入公4.2得到反射向量R

R = I – 2 ()N   4.6

(2) 反射向量求解方法

 

 

 

 

 

 

            4.4 反射求解

R进行平移,与向量N的延长线相交。因为入射角与反射角相等,此外IR的长度相等,所以有高中知识得到三角形△ION是一个等腰三角形。故有公式

ON = 2S   4.7

由矢量法则得到公式

R = I + 2S   4.8

S-IN上的投影,所以有

S = - N   4.9

因为N是单位向量,简化一下将得到公式

S = - (I·N)N     4.10

所以得到反射向量

R = I – 2()N  4.11

4.5折射向量构造

折射定律是几何光学的基本定律之一,折射效果如图4.5。确定过程中光的折射,折射光和入射光之间关系的法律。折射定义:光线从某一个介质进入另一个介质,光反射界面的一部分,此外另一部分光透过界面,在另一个介质折射,根据折射光线服从折射定律,折射光和入射光、法线将会在同一个平面上,折射光线和入射光将位于法线两侧:得出入射角正弦与折射角的正弦成正比,

 

 

 

 

 

 

 

 

 

 

 

               4.5 折射

  = n  4.12

公式4.12n是一个比例常数,我们称之第二介质相对第一介质的相对折射率。

折射的定义:光从一种透明介质斜射入另一种透明介质时,传播方向一般会发生某种变化,我们称这种现象叫光的折射。其中光的折射与光的反射会发生在这两种介质的交界处,只是反射光返回原介质中,而折射光则进入到另一种介质中,由于光在在两种不同的物质里传播的速度有所不同,故他们在两种介质的交界处传播方向将会发生变化,即光的折射。在光线跟踪程序设计中,当我们的场景中有了透明的物体比如水珠等,我们就要考虑到折射问题。图形渲染中加入折射规律,无疑将会给整个渲染场景带来非常逼真的效果。在本程序中简单设计了一个具有水的折射率的球体作为折射规律的研究。此外我们知道一个物体的折射率是固定不变的,水的折射率为1.33,金刚石2.42,水晶1.55,玻璃1.5

不过有时候会出现全反射的情况,我们需要加以考虑。先来介绍下什么是全反射,以及什么情况下会出现全反射的现象。

全反射:又称全内反射,指光由光密介质(即光在此介质中的折射率大的)射到光疏介质(即光在此介质中折射率小的)的界面时,全部被反射回原介质内的现象。具体解释:光由光密介质进入光疏介质时,要离开法线折射。当入射角θ增加到某种情形时,折射线延表面进行,即折射角为90°,该入射角θ称为临界角。若入射角大于临界角,则无折射,全部光线均反回光密介质,此现象称为全反射。当光线由光疏介质射到光密介质时,因为光线靠近法线而折射,故这时不会发生全反射。

此现象发生的条件:光必须由光密介质射向光疏介质;入射角必须大于或等于临界角

临界角公式为C = arcsin 4.13

借用此公4.13,我们需要在程序设计中考虑这个问题。

5 常见几何与射线求交算法

5.1平面与射线求交

大概平面与射线求交6][7][8是最简单的一种算法了,在这里平面是无限延展、不计大小、不及厚薄的,如图5.1。数学中的平面的定义都是抽象出来的,理想化的模型。我们定义一个平面方程为Plane :

Ax + By + Cz + D = 0   5.1

其中A,B,C系数是平面Plane的法线:N(A,B,C),当N是单位法矢量时,D是该平面至原点的垂直距离(可为正或负)。该方程在数学中称为点法式方程。我们有前面的一章节得知射线方程Ray:

Ray(x, y, z) = Origion(x, y, z) + t * Direction(x, y, z)  5.2

联立方程组式5.1、式5.2得到

N(origion + t * direction) + D = 0(N equals N(A,B,C),|N| = 1)  5.3

t <= 0 时即为未相交(t == 0时说明射线与平面平行)。由此,我们通过计算将会得到射线与平面的相交结果,并返回该交点的相交时间t,相交几何信息以及交点处的法线N(A,B,C)

 

 

 

 

 

 

5.1 平面与射线

5.2球体与射线求交

球体的定义一个半圆绕直径所在直线旋转一周所成的空间几何体叫做球体。球体的重要属性:球体的半径;球体的球心位置。射线与球体求交图5.2

球体的方程:

+ +   =   5.4

其中:O(,,)表示球体球心在世界坐标系下的位置,R表示球体半径。我们有前面的章节知识得知射线方程Ray:

Ray(x, y, z) = Origion(x, y, z) + t * Direction(x, y, z)  5.5

联立方程组式5.4、式5.5得到方程
 =   5.6

5.6展开并合并多项式得到一元二次方程:

 *  + 2 * t * Direction(x, y, z) *  +  -  = 0   5.7

其中,一元二次方程的系数:

a =   5.8

b = 2 * Direction(x, y, z) * Direction(x, y, z) *

    5.9

c =  -  5.10

那么一元二次方程的判断delt =  – 4 *a *c  5.11

判断是否相交依据:如果delt小于零,那么5.7无解,也对应由5.5构成的射线将与由5.4构成的球体不相交;如果delt等于零说明5.7有且仅有一个解,其解为

= = -    5.12

当且仅当t = =  > 0时,才会认为该射线与球体相交;如果delt大于零说明5.7有两个解,分别为

 5.13

 5.14

比较的大小取出最小值,t = MIN(,) ,当且仅当 t > 0时,才会认为该射线与球体相交。

相交点的其他信息:

交点处的位置IntersectVertexPos(x, y, z):在这里直接将t带入5.5求得

IntersectVertexPos(x, y, z) = Origion(x, y, z) + t * Direction(x, y, z) 5.15

相交点的法线信息IntersectVertexNormal(x, y, z):这里利用球体的特征求交点处的法线,即由球心指向交点并单位化即可求得交点处的法线得

IntersectVertexNormal(x, y, z) = IntersectVertexPos(x, y, z) -   5.16

注意:IntersectVertexNormal(x, y, z)需要单位化处理。

 

 

 

 

 

 

 

5.2 射线与球体求交

5.3三角形与射线求交

事实上,三角形也属于平面中的一种,但是三角形面并不是无限的,是有一定区域性的,这就为射线与三角形求交带来一定的复杂度。在本次程序设计中,我们采用了三角形重心填充的算法来求解相交问题。讲解三角形重心填充算法前,先介绍一下直线的隐式方程,因为三角形是由三条边构成的,每条边又是由直线段构成的,判断某一点是否位于三角形内部是至关重要的。

5.3.1直线的隐式方程求解

直线隐式方程求解如图5.3

直线方程的通式方程:

Ax + By + C = 0  5.17

先找到任意与(--)垂直的向量(A, B)。向量(A, B) = (--)就满足此要求,则5.16将得到

(-) * x + (-) * y + C = 0  5.18

又因为点()f(xy) = 0直线上,将()代入5.18

C = *  – *  5.19

5.19代入5.18得直线的隐式方程:

 (-) * x + (-) * y +*  – *  = 0  5.20

注意:此方程5.20隐含了直线的斜率,不涉及浮点数的除法问题。

 

 

注:  f(x, y) = (A,B)梯度向量

 

 

 

 

 

    5.3 直线隐式方程求解

5.3.2判断一点是否在三角形内部

由直线的隐式方程判断某点是否位于三角形内部。如右图5.4所示,判断一条射线Ray(x, y, z)与给定的三角形△ABC是否相交。根据上节中的直线隐式方程即可求得。

三角形三顶点表示:A(0)B(1) C(2)

 

 

 

 

 

 

5.4 射线与三角形求交

AB直线隐式方程:

(x, y) = (-) * x + (-) * y +  *  – *  5.21

BC直线隐式方程:

(x, y) = (-) * x + (-) * y +  *  – *  5.22

CA直线隐式方程:

(x, y) = (-) * x + (-) * y +  *  – *  5.23

△ABC的重心坐标为βγ)

α = (x, y) /  5.24

β= (x, y) /    5.25

γ = (x, y) /    5.26

其中α+β+γ = 1 (三角形重心坐标原理)举例说明,对于点A而言

α = () /  = 1

β = 0 (点A位于直线AC

γ = 0 (点A位于直线AB

由此根据三角形重心原理判断某点的αβγ是否同时大于0,若同时大于零则说明该点在三角形内部,即该射线与该三角形相交,否则不相交。我们最后求出交点的时间t,计算出交点位置,交点的法线可以取三角形三顶点的平均法线或线性插值法线。

三角形的重心插值实现核心伪代码:

float A = v0.x - v1.x

float B = v0.y - v1.y

float C = v0.z - v1.z

float D = v0.x - v2.x

float E = v0.y - v2.y

float F = v0.z - v2.z

float G = ray.direction.x

float H = ray.direction.y

float I = ray.direction.z

float J = v0.x - ray.origin.x

float K = v0.y - ray.origin.y

float L = v0.z - ray.origin.z

float EIHF = E * I - H * F

float GFDI = G * F - D * I

float DHEG = D * H - E * G

float determinant = A * EIHF + B * GFDI + C * DHEG

float beta = (J * EIHF + K * GFDI + L * DHEG) / determinant

if (beta <= 0.0f || beta >= 1.0f){

result.geometry = 0

return result

}

float AKJB = A * K - J * B

float JCAL = J * C - A * L

float BLKC = B * L - K * C

float gamma = (I * AKJB + H * JCAL + G * BLKC) / determinant

if (gamma <= 0.0f || beta + gamma >= 1.0f){

result.geometry = 0

return result

}

result.t = - (F * AKJB + E * JCAL + D * BLKC) / determinant

if (result.t < 0.0f){

result.geometry = 0

return result

}

5.4柱体与射线求交

圆柱体的定义:

旋转定义法:一个长方形以一边为轴顺时针或逆时针旋转一周,所经过的空间叫做圆柱体。

平移定义法:以一个圆为底面,上或下移动一定的距离,所经过的空间叫做圆柱体。

圆柱体的重要参数:圆柱体的底面半径R,圆柱的高H

圆柱方程:

 +  -   = 0   y∈[0, H]         5.27

射线方程:

Ray(x, y, z) = Origion(x, y, z) + t * Direction(x, y, z)    5.28

联立方程组式5.27、式5.28将会得到一个一元二次方程组,这与二次曲面体球体判断类似,故不在此说明求解过程。判断交点时还会判断交点的高度是否位于圆柱体高度H范围,求到的交点法线:由同一平面的圆心点指向交点并进行单位化后的向量。

5.5圆锥体与射线求交

圆锥体定义:圆锥也称为圆锥体,是三维几何体的一种,是平面上一个圆以及它的所有切线和平面外的一个定点确定的平面围成的形体。圆形被称为圆锥的底面,平面外的定点称为圆锥的顶点或尖端,顶点到底面所在平面的距离称为圆锥的高。

圆锥体的重要参数:底面半径R,椎体高度H

圆锥体的隐式方程:

 -  +   = 0   5.29

射线方程:

Ray(x, y, z) = Origion(x, y, z) + t * Direction(x, y, z)    5.30

联立方程组式5.29、式5.30,同样得到一个一元二次方程,这与二次曲面体球体判断类似,故不在此说明求解过程。此外交点处的法线:

N(,  - 2 * ( y - h),  ) 根据5.29求导数得到

5.6组合体与射线求交

现实生活中,我们所见到的大部分物体并不是规则的,所以有些物体并没有统一的标准方程供我们求解使用。但是,我们把比较法复杂的物体当做由基本图元三角形构成的几何体,那么这个难题也就迎刃而解了。那么,又有一个问题出现了,求交的无效次数变多了,性能就会降低。其中包围盒算法是一种求解离散点集中最优的包围空间的方法,射线与组合体求交如图5.5。算法的基本思想是用体积略大且特性简单的几何体(称为包围盒)来近似地代替比较的复杂的几何对象。在这里我们采取球体包围盒来加速组合体的求交速度。以组合体的重心为球体包围盒的中心,以组合体的最大轮廓半径为包围球的半径,进行射线与组合体的求交的优先判断。

 

 

 

 

 

5.5 射线与组合体求交

6 光栅化渲染器的设计实现

一个好的图形引擎都会有一个良好的光栅化渲染器。讲到光栅化的过程大都包含以下内容:

创建渲染器Renderer树状图

创建帧缓存深度信息(ZBuffer)Frame/depth(z)buffer

设置场景的摄像机以供投影、视图矩阵操作 Projection/View matrix

创建模型即场景中的物体 Scene models

创建顶点、索引缓存以供模型文件加载 vertex/index buffer

创建纹理以供加载纹理贴图时所需要的纹理资源texture

渲染场景中的模型model

遍历模型model中的所有三角形,对每个三角形执行以下操作

对三个顶点分别做模型矩阵变换实现坐标系转换即调用顶点着色器vertex shader

背面剔除操作舍弃不在视线范围内的三角形 backface culling

光栅化三角形

    计算三角形的包围盒,对于包围盒内的每个像素点执行以下操作triangle bounding box

像素测试判断该点是否在三角形内部 pixel test

线性插值包括透视校正 perspective correct interpolation

深度测试 depth/z buffer test

加入光照的相关计算 blinn-phong model lighting

纹理等采样以及过滤 texture filtering

写入帧缓存frame/depth write

线框模式渲染

直线画线算法 bresenham’s line algorithm

写入帧缓存frame/depth write

帧缓存写出生成图像图片 frame buffer output

上面的文字描述即是一个比较完整的光栅渲染器的过程,其中最核心的算法是场景模型中每个三角形的光栅化,在下节将详细介绍三角形的光栅化原理。

7 三角形光栅化原理解析

谈到三角形的光栅化原理,不得不说三角形的重心填充算法,实际上在射线与三角形求交的那部分也讲解到了三角形重心知识。在这里将详细讲解在计算机图形学中是如何光栅化三角形的。

对于场景中的模型网格,我们可以将它们统一归为以三角形基元组合而成。因为把三角形作为几何图元处理的好处有如下几点:

(1) 三角形是表面的分段线性的有效逼近,如同用多条相连的线段分段去逼近一个函数或者曲线

(2) 三角形是最简单的多边形。小于3个顶点就不能构成一个表面。

(3) 三角形是平坦的。含有4个或以上顶点的多边形不一定是平坦的,因为其前三个顶点能定义一个平面,第四个顶点或许会位于该平面之上或之下。

(4) 三角形经过某些变换之后属性仍然是三角形,这对于放射变换以及透视变换也成立。最坏的情况下,从三角形的边去看,三角形有可能会退化为线段。在其他角度观察,仍能维持是三角形。

(5) 几乎所有的商用图形加速硬件都是为三角形光栅化而设计的。从比较传统的PC计算机三维图形加速器开始,其中的渲染硬件只专注于三角形的光栅化而设计。此决策还可追溯到最早期使用的软件光栅化的三维游戏,所以,基于三角形的技术已经牢牢确立在游戏业界,在未来几年应该不会有所大的改变。9

在进行光栅化三角形的时候,我们会先遍历由三角形确定的包围盒(对应的坐标xMinxMaxyMinyMax)内的每个像素点,通过利用直线的隐式方程计算出该点的重心坐标βγ)来判断该点是否位于三角形内部。

(1) 当且仅当αβγ同时大于零表明该点位于三角形内部

(2) αβγ其中一个为零时表明该点位于三角形的某个边上(边界处理)

(3) αβγ其中两个为零时表明该点位于三角形的某个顶点上(边界处理)

除了123)否则该点位于三角形外部,舍弃该点的其他操作。其中α+β+γ= 1,在我们计算重心坐标时,可以只计算出前两个坐标αβ,第三个坐标用γ = 1 - α - β得出,这样减少了一部分计算量进而提高了一定的性能。

当我们用重心插值时,我们可以对三角形三个顶点的位置插值,我们同样也可以对三个顶点的法线进行插值,这样得到的法线再加上Phong模型光照将会得到更加逼真光滑的模型渲染效果,此外,我们还可以对纹理插值,△ABC光栅化如图7.1。对于给定的三个顶点的纹理坐标信息UV,我们可以就像插值顶点一样插值纹理坐标。不过在纹理进行插值时,我们需要纠正由透视导致的不正确纹理插值10。具体原因以及解决方案并不在本课题研究范围之内,故在此不再详细描述。

 

 

 

 

 

 

   

 

     7.1 △ABC光栅化

8 纹理

在三角形光栅化时,也有纹理11的线性插值12。为模型添加纹理效果,会极大提高模型的真实性。

8.1 BMP纹理资源简介

“BMP图片,是位图Bitmap的简称,它是Windows下,任何格式的图片文件(包括视频播放)都要转换为位图才能显示出来。各种格式的图片文件也都是在位图的基础上采用不同的压缩算法生成的。位图种类可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选1bit4bit8bit24bitBMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

位图内容主要部分分为如下4个部分:

(1) 文件信息头。简称BITMAPFILEHEADER,位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;

(2) 位图信息头。简称BITMAPINFOHEADER,位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;

(3) 调色板。其中这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;

(4) RGB通道(在WindowsRGB颜色矩阵的存储格式是BGR),位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。13

我们通过这个存储结构将纹理信息读入到一个指针数组中,为纹理贴图提供纹理资源。

8.2 UV纹理坐标

在游戏等要求真实度很高的场景中,我们都会为模型贴上纹理,增强场景的真实度。纹理贴图其实并不深奥,我们只要有模型顶点的纹理坐标信息值映射到我们的纹理资源中去,将该点的纹理信息作为光照信息中的漫反射因子,换句话说也就是纹理与光照之间的融合。有时候,有的模型并没有UV坐标14信息,这就需要我们构建模型的时候自己创建,当然像Maya3ds Max等的软件还是可以自动创建模型的UV坐标信息,UV坐标图解8.1

我们有了纹理资源,也有了UV坐标信息,接下来就是利用上节所提到的三角形重心原理进行纹理的线性差值,具体如下:当我们为要光栅化三角形内部的某个顶点时,我们已经计算出来了该点的的权值αβγ,已知三角形三个顶点的UV坐标: 

则该点VertexUV坐标:

u = α *  + β *  + γ *

v = b *  + β *  + γ *

最后我们再根据该点纹理坐标去访问所对应的纹理资源信息,加以Phong等光照模型进行渲染计算,最后完成纹理贴图的效果。

 

 

 

 

 

8.1 UV坐标图解

8.3 纹理贴图采样的几种算法

我们知道计算机只能处理01,即只能处理离散数据。然而,在现实的生活世界里我们很少会处理离散的数据,大部分都是一些连续的数据,比如声音、时间等。基于这种原因,我们计算机图形学纹理渲染必然会出现相应的走样(也称为锯齿)问题,这种现象是无法避免的,只能通过反走样(Anti-alias)、多次采样(Sampling)来削弱走样所带来的问题。解决走样问题的方案实际上有很多种,反走样已经从离线渲染逐步普及到了实时渲染的领域,在这里我们将简单介绍几个常用到算法。

(1) Super Sampling Anti-Aliasing,简称SSAA,在很多的游戏渲染设置中常会遇见。具体的实现方法就是渲染一张大图,对每个最终像素点的内部做了一个均匀分布采样。更加通俗地说,每个像素分布多个采样点,这些采样点可以是均匀分布、泊松分布(Poisson distribution)、随机分布、抖动分布等。每个采样点都有单独的颜色值、深度值,对应的像素着色器在每个采样点都执行一遍。如图8.2所示,有2个白色,1个红色,13个黄色的采样点,那么根据SSAA算法得知,最后这个像素点的值是这16个采样点的平均值,即  = ((111)* 2 + (100)* 1 + (110) * 13) / 16

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

8.2 SMAA采样

注:一个像素内部的采样点分布 16个蓝圈代表16个采样点其中红色和黄色是覆盖了这个像素的两个三角形

(2) Multi-Sampling Anti-Aliasing,简称MSAASSAA方案需要在每个采样点都执行一次像素着色器(pixel shader),并且保存它们的颜色值和像素值,当然这样做在时间与空间上的开销都是很大的。Multi-Sampling Anti-Aliasing的出现极大地改善了这点。MSAA在计算每一个像素时,我们会只执行一次像素着色器的运算,之后我们将把输出颜色写入到所有通过深度测试的样本中。注意,像素着色器的输入属性可以是像素点的中心也可以是三角形所覆盖的所有采样点的中心。如图8.3所示,

 

 

 

         

 

 

 

         

                   颜色采样color sample

                    深度采样depth sample

                8.3 MSAA采样

诸如其他的采样例子:Coverage Sampling Anti-Aliasing简称CSAAMorphological

Antialiasing简称MLAAFast Approximate Anti-Aliasing简称FXAADirectionally Localized Anti-Aliasing简称DLAASubpixel Reconstruction Anti-Aliasing简称SRAA等等,关于采样的方法还是有很多种。

现在介绍本程序设计中使用到的采样算法。我们首先写出一个光栅渲染器,将每条达到物体的射线采取该像素点周围多个射线进行采样,来提高我们生成图像的质量。

在本程序采样算法中使用了规则采样与非规则采样,可以在预编译中设置该选项。

 

 

 

 

 

 

8.4规则采样                8.5非规则采样

实际上计算一个像素点的平均颜色,我们是需要对该像素点采取统一的算法。这里有几种算法可以实现这一目的。典型的算法,对采样点(xy)∈[0,1] X [01],然后将(i - 0.5j - 0.5)应用于(xy)即覆盖该像素点的区域,如上图8.48.5所示。如果使用随机数生成该像素点方形覆盖区域的点进行颜色平均,这是一种非常简单的方法。然而,随机数采样有个难点就是,如果我们不幸运的话,采样点随机生成的周围点都集中在一个很小的方格中,再用这些点的平均值作为结果,将出现与未采样前一样的效果,这不但降低了性能反而还没有取得采样的目的,这不是我们最终的目的。所以,这里我们将像素的周围分割成很多的网格,再在这些对应的众多网格中假如先平均9份,在这9份中的每一份中随机产生一个待平均的种子点,这样做的话就不会出现很多随机点聚集在一块的情况了。如下图8.68.7所示。

 

 

 

 

 

 

8.6有问题的随机采样                      8.7随机数产生有问题时以及解决方案

此外,我们也有泊松采样。其实,泊松采样也是一种随机采样,不过对于每个待平均的点都会执行一次距离检查,比如该点与其它任何一个点的距离小于特定值r时,会舍弃该待平均点,继续重新随机生成一个待平均点,同样还会再次执行判断,这样同样实现了上面的那种算法的效果。如下图8.8所示

 

 

 

 

 

 

8.8泊松采样

9 三角形网格数据构造实现

在现实世界中的大多数模型,都是由很多共享顶点的三角形所构成,正如我们前节所讲到的三角形光栅化,足以看出三角形是多么重要的模型数据。事实上,对于现代工业上经常使用到的模型如objoffstlply等文件,大部分数据都是以三角形为基元进行组合成一个非常复杂的模型。这些模型文件有最基本的顶点位置信息;有的有顶点法线信息或面的法线信息或者没有法线信息(因为面法线信息可以根据三个顶点所构成向量的叉积求得该平面的法线信息);如果该模型有纹理资源,那么该模型文件中的每一个顶点还会存储对应的uv坐标值,以供纹理插值使用;有的会附带顶点属性信息比如off模型文件等。

在本节中,我们将介绍一种管理这些模型中的数据的方案,来尽可能得降低程序内存的使用率。其中的一个简单的三角形化网格数据结构设计如图9.1

 

 

 

 

 

0

1

2

                                                                           

 

 

 

3

2

1

 

 

 

 

 

 

 

 

 

1

0

3

 

 

 

 

 

9.1一个简单的三角形化网格数据结构

注:具有三个三角形组合而成的mesh网格,共四个顶点

你可以存储这些三角形作为一个独立的实体,就如C++中的对象一样,把物体抽象出来。在该图中我们存储了点三次,其它点分别为两次,总共存储了九个点。此外,你还要为每个独立的三角形存储一个指向纹理对象的指针。当然会有一个非常高效的存储,共享顶点数据,这样的话就只用存储四个顶点和一个指向纹理的指针。

核心伪代码如下

Class triangle

Texture* t

Vector3

取而代之使用两个类

Class mesh

Texture* t

Array of Vector3 vertices

And

Class meshtriangle

Pointer to mesh meshptr

int

其中是顶点数组对应的索引值。任何一个这样的三角形类或Mesh类都是这样工作的。那么还有空间存储更优化的Mesh类吗?典型地,一个大的模型数据中一个顶点将会被六个三角形面所共享,专业地说该点的度一般为6这在一些大模型中就是这样,尽管也有其他值的度。这也就意味着大约两个三角形彼此有一个共享的顶点,换句话说你有n个三角形,那么就会有n / 2个共享顶点,以及3 * n个未共享的情况,当你共享的时候,你需要额外的3 * n个整型值和n个指针,因为你不用为每个三角形再存储纹理指针了,这样的话我们将会节省n个指针所带来的空间开销。

每个顶点会有材质参数,纹理坐标,发光系数,实际上这些参数都是为渲染模型所使用。实践中,这些参数信息将会通过三角形的双线性插值(重心填充光栅化算法),即每一个顶点插值后会得到一个重心坐标γ),并且使用同样的方式将(u, v)坐标插值到顶点。

一个应用于(uv)的简单公式

P(βγ) =  + β( –)+γ * ( - )   9.1

u(βγ) =  + β(-) + γ(-)   9.2

v (β, γ) = v0 + β(-) + γ(-)   9.3

存储顶点的另一种数据就是顶点的法线信息。我们同样可以用这种算法插值法线坐标(u, v)来实现光滑,而不像面着色模式,这种插值法线得着色方式我们称之为Phong shading

10加速光线跟踪求交的重要算法包围盒的设计与实现

本节我们将介绍层次包围盒算法15][16][17——Bounding Volume Hierarchies algorithm简称BVH算法。我们知道,光线跟踪中求交的次数是跟场景中模型的数量有直接关系的,如果不采取降低无效的求交次数,无疑将会大大降低整个程序的运行性能。事实上在求交上,大部分模型都是无效的,所以我们想出来一个包围盒,将众多小的模型包围起来一次性判断求交,当只有与包围盒有效求交时,才会进入包围盒内部的物体再进行判断求交。层次包围盒的最基本的算法是取轴对齐层次包围盒,如下图10.1所示。

 

 

10.1一条射线仅仅先测试该包围盒求交是否有效

然而,射线与这种包围盒求交的话,实际上会比那种蛮力搜索求交的方法花费更多时间。因为,测试射线与这种包围盒求交的代价有可能会很高,比如当包围盒中的物体过多时,我们有可能要遍历包围盒中的很多物体。不过,如果射线与这个包围盒求交失败了,那么我们就无需计算包围盒内众多的所有物体。所以我们采用把这种包围盒分成众多若干个这样的包围盒,也即将场景中的众多物体一部分一部分细分,再加上包围盒。如下图10.2所示。

 

 

 

 

 

 

 

10.2包围盒中嵌入包围盒

那么构造这种的包围盒的数据结构就像是一棵树,其中树根是这个大的包围盒,左边和右边的树叶子集是两个小的包围盒。如下图10.3所示

 

                                       

10.3三个蓝球属于大的包围盒,三个红球属于小的包围盒

核心伪代码:

If(ray hits root box) then

If(ray hits right subtree box) then

check three triangles for intersection

If(ray hits left subtree box) then

check three triangles for intersection

if(an intersections returned from each subtree) then

return the closest of the two hits

else if(an intersection is returned from exactly one subtree) then

return that intersection

else

return no intersection

else

return no intersection


11总结与展望

在本次程序设计中,我将光线跟踪中的基本算法基本实现,生成了一幅比较逼真的图像。不过该程序设计还有很多未实现的高级算法,不尽人意。比如说:

(1) 更多的复杂几何图形。本程序设计中未涉及到NURBS等曲面也就是非规则的模型表面,然而在现实生活中我们常常会遇到曲面的情况比如游戏中的汽车模型。

(2) 光照计算的算法很简单。本程序设计中只涉及到点光源,没有平行光源的参与,然而在现实中我们白天的太阳光就是一个平行光的例子。

(3) 本程序的性能不高你会在附录中看到有时候生成一幅高质量图像时会需要2分钟多甚至更多的时间。由于是基于CPU下的光线跟踪软实现,即没有GPU硬件参与编程,性能自然会很低,现代的图形渲染一般都会选择GPU进行加速渲染。

然而通过这次程序设计,我不但提高了对计算机图形学的深刻认识,更提高了我的实际编程能力。有时候,看似很明白的理论,到了真正编程实现的时候可能会出现低端的认知错误,导致程序开发进度缓慢;有时候,看似简单的程序却没有做好调查、分析、设计,直接写代码,很容易做返工处理,这对于我们来说都是深恶痛绝的。所以,对于一门实践性与理论性很强的学科,我们应该始终坚持谨慎、负责的态度。这对我未来的工作,将会有一个很好的图形学底层知识铺垫。我将借助这一平台不断探索更多的知识,走向未来,走向成功。

参考文献

[1] R.Keith Morley A K Peters.Fundamentals of Computer Graphics 3ed[M].CRC Press.2009:68-92.

[2]杨丰萍.统一建模语言UML及其支持工具[J].华东交通大学学报.2000(03) : 59-77.

[3]吴建峰,汪毅.统一建模语言UML及其应用[J].华北航天工业学院学报.2002(04) : 19-33.

[4]张卫山,巫家敏,严新民.基于UML的管理信息系统开发 [J].计算机工程,1999(12) : 88-97.

[5]王小彬,焦圣喜,祝永志.C语言实现三维图形 [J].吉林化工学院学报,2003(01) : 9-17.

[6]Moller T. A. A fast triangle-triangle intersection test [J].Journal of Graphics Tools19972(2): 25-30.

[7]Held M. ERIT. A collection of efficient and reliable intersection tests [J].Journal of Graphics Tools19972(4): 25-45.

[8]Chazelle B. An optial algorithm for intersecting three-dimensional convex polyhedral [J].SIAM Journal on Computing199221(4): 671-697.

[9]Jason Gregory.游戏引擎架构[M].北京.电子工业出版社2014364-390.

[10]韩红蕾,王文成.光删化时的线性纹理插值[J].计算机辅助设计与图形学学报,2011236-10.

[11]Kim DKim L S. Area-efficient pixel rasterization and texture coordinate interpolation[J].Computers & Graphics200832(6):669-681.

[12]Juan Pineda. A Parallel Algorithm for Polygon Rasterization [J].Computer Graphics198822: 4.

[13]Donald Hearn , M Pauline Baker. Computer Graphics with OpenGL[M].Third Edition. Publishing House of Elec2004620-626.

[14]Heckbert P S. Survey of texture mapping [J].IEEE Computer Graphics and Appliations19966(11): 56-67.

[15]黄文钧,穆玉洁.新包围盒技术[J].广西民族学院学报(自然科学版),1998(01) : 159-297.

[16]赵爽,李学军.复杂场景的快速跟踪算法[J].计算机工程,2006(01) : 19-23.  

[17]高军峰,徐凯声,崔劲.一个基于包围盒技术提高光线与物体求交效率的算法 [J].交通与计算机,2004(06) : 33-39.


致谢

在这里主要感谢我的导师对我的指导以及帮助我完成毕业论文的审核。感谢学院系里提供的实验室环境,借助2016年的国庆时间在实验室把本次毕业设计的主要程序完成。


附录

本次程序设计最初在Visual C++6.0编译器下编辑,测试是在Visual Studio 2013Release

运行环境:

电脑型号:华硕 X550VC 笔记本电脑

操作系统:Windows 10 专业版 64( DirectX 12 )

处理器:英特尔 第三代酷睿 i3-3110M @ 2.40GHz 双核

主板:华硕 X550VC ( 英特尔 Ivy Bridge - HM76 Express 芯片组 )

内存:4 GB

主硬盘:东芝 MQ01ABD050 ( 500 GB / 5400 /)

显卡:英特尔 HD Graphics 4000 ( 32 MB / 华硕 )

运行的的结果是一张BMP图片,由于不同的采样等命令截图如下

 

附图1 光线跟踪中一个灯光的效果

附:递归深度:10 光线追踪条数: 977379 耗时:4s

 

 计算机图形学之光线跟踪算法的研究与实现2017年我的优秀毕业论文_第1张图片

 

 

 计算机图形学之光线跟踪算法的研究与实现2017年我的优秀毕业论文_第2张图片

 

 

 

 

 

 

附图2光线跟踪中两个灯光的效果

附:递归深度:10 光线追踪条数: 977379 耗时:5s

 

 

 

 

 

 

 

 

 

 

 

附图3反走样2x2 非规则采样效果

附:递归深度:10 光线追踪条数: 3900509 耗时:12s

 

 

 

 

 

 

 

 计算机图形学之光线跟踪算法的研究与实现2017年我的优秀毕业论文_第3张图片

 

 

 

附图4反走样8x8 非规则采样效果

附:递归深度:10 光线追踪条数: 62399952 耗时:2min48s

 

 

 

 

 

 

 

 

 

 

 

附图5反走样8x8 规则采样效果

附:递归深度:10 光线追踪条数: 62395739 耗时:2min58s

 


你可能感兴趣的:(计算机图形学算法)