摘要:光线跟踪属于计算机图形学的经典理论,在高质量的场景渲染领域有着很广泛的应用,虽然读研没在图形所,但还是非常有幸的上了学校里大牛老师的课,俗话说,没吃过猪肉,还没见过猪跑么,呵呵,开玩笑,下面的文章以作者研一上课图形学大作业为背景,因为最近要增加博客的点击率,所以把陈年的谷子给翻了出 来,下面进入正题,呵呵。
第一部分: 原理部分
光线跟踪是模拟从照相机(人眼)位置发射一条光线,穿过屏幕,和虚拟世界的物体相交与一点,光线跟踪的主要任务就是计算相交点的亮度值,相交点的亮度由两部分组成,一是直接光源的照射,二是其它物体表面的反射。如图所示:

图1 照相机发射光线穿越屏幕
图2 照相机发射光线与物体相交处的亮度计算
光线到达物体后有两部分运算:
光照计算(局部与全局)
一部分是局部光照计算,包括漫反射,镜面反射,环境光等。这部分主要采用phong模型来计算局部光照强度。
漫反射,从光源发出的光到达物体后沿着各个方向均匀反射,这里主要要计算物体交点的法向量及此点与光源的连线的夹角余弦值。然后可以算出亮度值。如下图所示:
图3漫反射效果图
镜面反射沿着出射光线方向偏离越远光线越暗,如下图所示,L为入射光线,R为反射光线,V为视点与交点连线,V看到的点亮度与V R的夹角余弦值成正比:

图4 镜面反射模型
另一部分是全局光照计算,包括反射,折射等。全局光照计算主要涉及到光线的递归,为了防止光线在物体上不停的折射反射,一般都设置一个递归深度,当达到递归深度后不再进行计算。
在计算过程中有三种情况会终止光线的跟踪,一种情况是光线被物体挡住,此时可以终止计算此点像素值,返回一个(0,0,0)的颜色值,另外一种情况是直接照射到光源,此时也会停止光线跟踪,返回光源颜色。第三种情况是到达递归深度时仍然没有达到上面两个条件,此时强行终止光线的计算。跟踪深度越大,效果越逼真,计算量也越大。
在跟踪过程中需要计算光线与物体是否有交点,在本例中只计算了与球体及平面的交点,
球:
设c为球的中心,p为光线在屏幕的发出点,u为光线的方向,r为圆的半径,q为pq 与cq垂直的交点,得到一公式,alpha = -1 * (p - c) * u,此处有p + alpha * u = q. 然后计算q与c的距离b. a * a = r * r – b * b, 求出a, alpha = alpha – a;此处p + alpha * u即为射线与球的交点。
平面:
起点为p,u为方向,q = p + alpha * u;
Alpha = (d – p * n) / (u * n), d为平面的常量 其中x * n = d, n为法向。
第2部分:程序设计
程序的设计分为三部分:
第一部分设计了常用的一个类Vector3,并作了常用的运算符重载。类部分代码如下


1 class Vector3
2
3 {
4
5 public:
6
7 Vector3(double x, double y, double z) :
8
9 m_x(x), m_y(y), m_z(z){}
10
11
12
13 Vector3() :
14
15 m_x(0), m_y(0), m_z(0){}
16
17
18
19
20
21 Vector3 operator = (Vector3 Vector3)
22
23 {
24
25 this->m_x = Vector3.m_x;
26
27 this->m_y = Vector3.m_y;
28
29 this->m_z = Vector3.m_z;
30
31
32
33 return *this;
34
35 }
36
37
38
39 bool operator ==(Vector3 b)
40
41 {
42
43 if(this->m_x == b.m_x && this->m_y == b.m_y && this->m_z == b.m_z)
44
45 return true;
46
47 else
48
49 return false;
50
51 }
52
53
54
55 Vector3(Vector3& ax)
56
57 {
58
59 m_x = ax.m_x;
60
61 m_y = ax.m_y;
62
63 m_z = ax.m_z;
64
65 }
66
67
68
69 Vector3 Normalize()
70
71 {
72
73 double result = sqrt( pow(m_x, 2) + pow(m_y, 2) + pow(m_z, 2));
74
75 m_x = m_x / result;
76
77 m_y = m_y / result;
78
79 m_z = m_z / result;
80
81 return *this;
82
83 }
84
85
86
87 double operator * (Vector3 v3)
88
89 {
90
91 double result = 0;
92
93 result = this->m_x * v3.m_x + this->m_y * v3.m_y + this->m_z * v3.m_z;
94
95 return result;
96
97 }
98
99
100
101 Vector3 operator * (double f)
102
103 {
104
105 Vector3* vv3 = new Vector3();
106
107 vv3->m_x = this->m_x * f;
108
109 vv3->m_y = this->m_y * f;
110
111 vv3->m_z = this->m_z * f;
112
113
114
115 return *vv3;
116
117 }
118
119
120
121 Vector3 operator -(Vector3 v3)
122
123 {
124
125 Vector3* vv3 = new Vector3();
126
127 vv3->m_x = this->m_x - v3.m_x;
128
129 vv3->m_y = this->m_y - v3.m_y;
130
131 vv3->m_z = this->m_z - v3.m_z;
132
133
134
135 return * vv3;
136
137 }
138
139
140
141 Vector3 operator +(Vector3 v3)
142
143 {
144
145 Vector3* vv3 = new Vector3();
146
147 vv3->m_x = this->m_x + v3.m_x;
148
149 vv3->m_y = this->m_y + v3.m_y;
150
151 vv3->m_z = this->m_z + v3.m_z;
152
153
154
155 return * vv3;
156
157 }
158
159
160
161 public:
162
163 double m_x, m_y, m_z;
164
165 };
166
167
第二部分设计了一个虚基类用于被各种物体继承,这样设计主要是为了光线跟踪时对物体的遍历方便,同时设计了材质属性类,球体类,平面类等。


1 class Primitive
2
3 {
4
5 public:
6
7 enum{
8
9 SPHERE = 1,
10
11 BOARD
12
13 };
14
15 virtual int InterSect(Ray* ray, double& dis) = 0;
16
17 Material* GetMaterial() { return &m_Material; }
18
19 virtual Vector3 GetNormal(Vector3 Pos) = 0;
20
21 virtual Color Get_Color(){return m_Material.Get_Color();}
22
23 virtual void Light(bool light){ m_Light = light;}
24
25 bool IsLight() { return m_Light; }
26
27 virtual int GetType() = 0;
28
29 void SetName(std::string& name){m_Name = name;}
30
31
32
33
34
35 private:
36
37 Material m_Material;
38
39 bool m_Light;
40
41 std::string m_Name;
42
43 };
44
45
46
47
48
49
50
51 //here is a sphere
52
53 class Sphere : public Primitive
54
55 {
56
57 public:
58
59 int GetType() {return SPHERE;}
60
61 Sphere(Vector3 center, double radius){
62
63 m_Center = center;
64
65 m_Radius = radius;
66
67 }
68
69
70
71 double GetRadius(){return m_Radius;}
72
73 int InterSect(Ray* ray, double& dist);
74
75 Vector3 GetCenter(){return m_Center;}
76
77 Vector3 GetNormal(Vector3 pos){
78
79 Vector3 v3 = pos - m_Center;
80
81 NORMALIZE(v3)
82
83 return v3;
84
85 }
86
87
88
89
90
91 private:
92
93 Vector3 m_Center;
94
95 double m_Radius;
96
97
98
99 };
100
101
102
103 //board such as walls , floors ceilings and so on
104
105 class Board : public Primitive
106
107 {
108
109 public:
110
111 int GetType(){return BOARD;}
112
113 Board(Vector3 nor, double d)
114
115 {
116
117 m_Normal = nor;
118
119 m_D = d;
120
121 }
122
123 int InterSect(Ray* ray, double& dist);
124
125
126
127
128
129 double Get_D(){return m_D;}
130
131 Vector3 GetNormal(Vector3 pos){
132
133 m_Normal.Normalize();
134
135 return m_Normal;
136
137 }
138
139
140
141 private:
142
143 Vector3 m_Normal;
144
145 double m_D;
146
147 };
第三部分渲染引擎的设计


1 class Engin
2
3 {
4
5 public:
6
7 Engin();
8
9 ~Engin();
10
11 Scene* GetScene(){return m_Scene;}
12
13 Primitive* RayTrace(Ray* ray, Color& color, int depth, double r_index, double& dist );
14
15 void InitRender();
16
17 bool Render();
18
19 void SetTarget(int m_Width, int m_Height, int*** m_Buffer)
20
21 {
22
23 m_Width = m_Width;
24
25 m_Height = m_Height;
26
27 buffer = m_Buffer;
28
29 }
30
31
32
33
34
35 private:
36
37 Scene* m_Scene;
38
39 int m_Width, m_Height, m_CurrentLine, m_Pos;
40
41 int*** buffer;
42
43 };
最终效果图如下:
