零、一些效果图
如下所示:
一、光线跟踪的基本原理(引用)
光线跟踪(Ray-trace)是一种真实感地显示物体的方法,该方法由Appel在1968年提出。光线跟踪方法沿着到达视点的光线的相反方向跟踪,经过屏幕上每一象素,找出与视线所交的物体表面点 P0,并继续跟踪,找出影响P0点光强的所有的光源,从而算出P0点上精确的光照强度。
如上图所示,联结观察点和屏幕上的一个象素,即形成一根视线。因此,视线的数目等于象素的数目。对于每一根视线作如下处理:
计算视线V与各平面的交点。以距离最小的交点为可见交点P0。视线V在P0处产生反射和透射,所产生的反射线和透视线作为新的视线与各平面求交几时出新的交点P1、P2,并分别产生新的反射线和透视线,……。这样不断递归,直至所产生的视线射出场景。结果是得到视线跟踪轨迹上的一系列交点:P0、P1、P2、…、Pn。这个过程可以表示为一棵光线跟踪树。
下图所示是一棵与上图对应的光线跟踪树。树的结点代表物体表面与跟踪线的交点。结点连线代表跟踪线。每个结点的左儿子代表反射产生的跟踪线(r),右儿子代表透射产生的跟踪线(T)。空箭头表示跟踪丝射出场景。P0处的光强是P0、P1、P2、P3点光强的合成。计算方法是以后序周游的算法遍历这颗光线跟踪树。在每一结点处,递归调用光照模型,算出跟踪射线方向的光强,并按两表面交点之间的距离进行衰减后,传递给父结点。如此上递,最后得出P0点处的光强,亦即得到屏幕象素处的亮度。
二、光线跟踪算法的优缺点(摘录)
用光线跟踪方法显示真实感图形有如下优点:
1)显示它不仅考虑到光源的光照,而且考虑到场景中各物体之间彼此反射的影响,因此显示效果十分逼真。
2)有消隐功能
采用光线跟踪方法,在显示的同时,自然完成消隐功能。而且,事先消隐的做法也不适用光线跟踪,因为那些背面和被遮挡的面,虽然看不见,但仍榀能通过反射或透射影响着看得见的面上的光强。
3)有影子效果
光线跟踪能完成影子的显示,方法是从P0处向光源发射一根阴影探测光线。如果该光线在到达光源之前与场景中任一不透明的面相交,则P0处于阴影之中,否则,P0处于阴影之外。
4)该算法具有并行性质
每条光线的处理过程相同,结果彼此独立,因此可以大并行处理的硬件上快速实现光线跟踪算法。
光线跟踪算法的缺点是计算量非常大,因此,显示速度极慢。
三、程序代码及说明(本程序没有考虑折射效果)
getColourForRay()//获取射线ray的颜色,存入colour_out,最多跟踪traceNum次
void World::getColourForRay(const Ray& ray, Colour& colour_out,int traceNum)
{
if(traceNum != 0)
{
Object *obj;
Vec3 normal,hitpos,L,R,Ldir;
float dist,bf=0.5;//为了简化,这里将反射系数设为常数,其本来值与dist值有关
int i;
static int ligsize = m_lights.size();
obj = closestObject(ray,dist);//返回与光线ray相交的最近的物体,并将其距离存入dist中
if(obj)//检测光线是否与场景中的物体相交
{
Colour lightcolour,total1,total2,diffuse = Colour::black(),specular = Colour::black();
hitpos = ray.m_startPos;
hitpos.addMult(ray.m_unitDir,dist);
normal = obj->getGeometry().getNormalForPos(hitpos);
normal.normalise();
R = reflect(ray.m_unitDir,normal);//求出反射光线
for(i = 0;i < ligsize;i++)//求出每个光源对交点处光照的贡献
{
L = m_lights.at(i)->getPos() - hitpos;//求出阴影光线
L.normalise();
if(!closestObject(Ray(hitpos,L),dist))//检测阴影光线路径中是否存在遮挡物
{ //不存在遮挡物则累加上该光源对交点的散射和镜面光的贡献
lightcolour = m_lights.at(i)->getColour();
Vec3 H = (L - ray.m_unitDir)/2;
H.normalise();
diffuse += lightcolour * (L.dot(normal)>0?L.dot(normal):0);
specular += lightcolour * pow((H.dot(normal)>0?H.dot(normal):0),obj->getMaterial().ns);
}
}
total1 = ambient_lighting * obj->getMaterial().ka
+ diffuse * obj->getMaterial().kd + specular * obj->getMaterial().ks;
getColourForRay(Ray(hitpos,R),total2,traceNum-1);//递归计算下个交点的光照
colour_out = total1 + total2*bf;//累加
return;
}
}
colour_out = Colour::black();//其余情况返回黑色
}