转载请注明出处
欢迎留言、讨论、指正
本文未注明出处图片均来自 个人 或 Fundamentals of Computer Graphics(4th Ed)
计算机图形学最基本的功能就是渲染(rendering)一个三位物体:比如从某个特定视角看由很多几何形状组成的三维空间,所能看到的画面是什么样子?这是艺术家和工程师已经重复了几个世纪的工作。本质上来说,就是输入一堆对象,输出一个二维数组。总而言之,渲染考虑的就是每一个对象是怎么影响输出的图像的,分为两种:一种是对象顺序渲染(object-order rendering),按对象顺序依次考虑每个对象对所有像素的影响并更新像素值;一种是像素顺序渲染(image-order rendering) 按像素顺序依次考虑所有对象对每个像素的影响并更新像素值。两者循环顺序不同:
//-----对象顺序渲染----
for(object : world)
for(pixel : array)
{ pixel[i] = pixel[i]+ effect_of(object);}
//-----像素顺序渲染----
for(pixel : array)
for(object : world)
{ pixel[i] = pixel[i]+ effect_of(object);}
两种方法计算结果相同,但在不同应用场景性能不一样,我们将在第8章详细讨论有什么不一样。但通常来说,像素顺序渲染可能更简单,适应性更强,但产生一张“过得去的图像”所花费的时间更长。光线追踪的难点不在阴影和反射,而在于对象顺序的框架(object-order framework)。 ???我还没弄懂 T.T
我们先讨论像素顺序渲染,是因为光线追踪器能在不借助任何高级数学机制的条件下就可以工作。
基本的光线追踪算法
一个光线追踪器以此计算一个像素,每次计算的基本任务是图像当前像素点为视点(viewpoint),以互不平行的==“视线(viewing ray)”(射线)射出,“视线”先“击中(hit)”==的交点(intersection point)就是在图像相应像素点上应该显示的对象点,因为这个对象已经遮挡了它身后的所有对象。找到目标对象后,利用交点、法向量、和其他信息(如选择的渲染方式)进行 着色(shading)计算得到像素的颜色值。故光线追踪器由三个部分组成:
1.光线生成(ray generation) 根据相机的空间参数(camera geometry)计算每个像素上的“视线”的原点和方向;
2.光线相交(ray intersection) 找到视线首先击中的对象,计算法向量n;
3.着色(shading) 根据视线交点,法向量,和“视线”计算像素的像素的颜色值;
光线追踪器的真正潜力在学习了第10章的着色方法,第12章更先进的射线交互技术(ray intersection techniques),和第13章的渲染技术后才能发挥出强大的功能。
透视
艺术家早在计算机发明前几个世纪就在研究怎么将3D对象或景色用2D绘画表现出来,发展到今天比如相机鱼眼镜头,立绘。它们和计算机图形学具有共同的“工具”-----透视(perspective),3D对象的直线映射到2D图像中仍是直线。
最简单的投影是平行投影,3D空间的点朝投影方向(projection direction)移动直到hit上图像平面。平行投影在机械和建筑领域经常使用,因为保留与图像平面平行对象的大小和形状。
投影线相互平行且垂直于图像平面时,称作正交投影(orthographic projection)。有的书将投影线平行于坐标轴的投影称为正交投影。
投影线相互平行但不垂直于图像平面时,称作斜射(oblique)。
投影线交于视点称作透视投影(perspective perspective)。
平行投影的限制:人类的视角不只捕捉一个方向的光线,存在近大远小现象,平行线会在远处汇聚于消失点(vanishing point),透视能够解决这个问题。透视效果取决于视点的选择(而非投影方向)和图像所在平面。同平行投影一样,透视也分为正交和斜射两种。
你可能听说过传统的==“三点透视”==这种人工构建透视效果的系统(如下)。只要能遵循投影简单的数学原理就能达到透视效果。
视线计算
首先我们使用一条3D直线代表从视点e设向图像平面上的点s,P⃗=e⃗+t(s⃗−e⃗)\vec P=\vec e+t(\vec s-\vec e)P=e+t(s−e),则P是视线上一点,s⃗−e⃗\vec s-\vec es−e为视线的方向。t<0代表对象处于视点“之后”(使用t能保证被“看”到的对象始终视点前)。现在的问题时怎么找到点s。所有光线发射器坐落于直角坐标系(orthonormal coordinate frame),也被称作摄像机系(camera frame),原点在视点e,u⃗、v⃗和w⃗\vec u 、\vec v和\vec wu、v和w分别代表三个基向量,u⃗\vec uu右手正方向,v⃗\vec vv朝上正方向,w⃗\vec ww朝后正方向,右手系。
w⃗=a⃗∣a⃗∣ u⃗=b⃗×w⃗∣∣b⃗×w⃗∣∣ v⃗=w⃗×u⃗
\vec w =\frac{\vec a}{|\vec a|} \space \space \space
\vec u = \frac{\vec b\times \vec w}{||\vec b\times \vec w||} \space \space \space
\vec v = \vec w \times \vec u
w=∣a∣a u=∣∣b×w∣∣b×w v=w×u
正交视图
在正交视图下所有ray的方向都是−w⃗-\vec w−w,摄像机(图像平面)后面也可以有对象。所有的视线都从视点e和向量u⃗,v⃗\vec u,\vec vu,v决定的平面上平行射出,唯一需要确定的参数是图像平面在哪。我们定义lll为为左边界的横坐标,rrr为右边界的横坐标,bbb下边界纵坐标,ttt上边界正方向。
在3.2小节中我们介绍了nx×nyn_x\times n_ynx×ny图像像素坐标系。为将图片塞到(r−l)×(t−d)(r-l)\times(t-d)(r−l)×(t−d)大小的矩形中,像素水平间距为(r−l)nx\frac{(r-l)}{n_x}nx(r−l),纵向间距为t−bny\frac{t-b}{n_y}nyt−b,注意像素点均在中心,故(i,j)栅格图像坐标上的点的摄像机系坐标为:u=l+(r−l)(i+0.5)nxv=b+(t−b)(j+0.5)ny
u=l+\frac{(r-l)(i+0.5)}{n_x} \\
v=b+\frac{(t-b)(j+0.5)}{n_y}u=l+nx(r−l)(i+0.5)v=b+ny(t−b)(j+0.5)
在正交视角中,我们直接使用像素点作为视线的起点,由于视线方向已知,产生正交视图视线:
1.计算u,vu,vu,v
2.射线方向−w-w−w(斜射时w⃗\vec ww方向与视线d⃗\vec dd不重合)
3.射线起点=e⃗+uv⃗+vv⃗\vec e+u\vec v+v\vec ve+uv+vv
对于透视图,所有视线的起点均为视点,图像平面不再是视点e,而是视点前方图像平面距离(image plane distane) 或者称为焦距(focal length),同摄像机中的焦距作用一样。视线由视点和图像像素位置决定。视线产生的方法:
1.计算u,vu,vu,v
2.射线方向dd⃗d\vec ddd(斜射时w⃗\vec ww方向与投影线d⃗\vec dd不重合)
3.射线起点=e⃗\vec ee
视线-物体
我们已经产生了一个射线e+td⃗e+t\vec de+td,再来看看t>0的范围内hit到对象的情况。为了便于学习,使用简单例子:找到[t0,t1][t_0,t_1][t0,t1]之间hit到的第一个表面。
球体的情况
p(t)=e+td⃗p(t)=e+t\vec dp(t)=e+td,隐式表面f§=0,我们希望找到hit在表面上,带入可得f(e+td⃗)=0f(e+t\vec d)=0f(e+td)=0,圆的表达式为(x−xc)2+(y−yc)2+(z−zc)2=R2(x-x_c)^2+(y-y_c)^2+(z-z_c)^2=R^2(x−xc)2+(y−yc)2+(z−zc)2=R2,圆心C(xc,yc,zc)(x_c,y_c,z_c)(xc,yc,zc)那么我们将以上两个式子变换为:(e+td⃗−C⃗)(e+td⃗−C⃗)−R2=0(e+t\vec d-\vec C)(e+t\vec d-\vec C)-R^2=0(e+td−C)(e+td−C)−R2=0,化简如下:
(d⃗⋅d⃗)t2+2d⃗(e⃗−C⃗)t+(e⃗−C⃗)⋅(e⃗−C⃗)−R2=0(\vec d\cdot\vec d)t^2+2\vec d(\vec e-\vec C)t+(\vec e-\vec C)\cdot(\vec e-\vec C)-R^2=0(d⋅d)t2+2d(e−C)t+(e−C)⋅(e−C)−R2=0
判别Δ=d⃗⋅(e⃗−c⃗)2−(d⃗⋅d⃗)((e⃗−C⃗)⋅(e⃗−C⃗)−R2)\Delta=\sqrt{\vec d\cdot(\vec e-\vec c)^2-(\vec d\cdot\vec d)((\vec e-\vec C)\cdot(\vec e-\vec C)-R^2)}Δ=d⋅(e−c)2−(d⋅d)((e−C)⋅(e−C)−R2)
t=−d⋅(e⃗−C⃗)±Δd⃗⋅d⃗t=\frac{-d\cdot(\vec e-\vec C)\pm\Delta }{\vec d\cdot \vec d}t=d⋅d−d⋅(e−C)±Δ
实际使用过程中应当先检查判别式再做其他工作,当我们需要判断 hit 结果的时候,通常作为更复杂对象的边界,检查判别式已经足够了。
同2.5.4章所讨论的那样