我一直在研究一个类的光线跟踪算法,并遇到了一个奇怪的问题。
这是我正在研究的基本场景。什么都不重要,一切都好。现在,我的代码就这样组织起来了:
Image* Tracer::render()
{
int w = _scr.width();
int h = _scr.height();
Image* im = new Image(w, h);
int i, j;
for(i = 0; i < h; i++)
{
for(j = 0; j < w; j++)
{
im->setColour(i, j, render(i, j));
}
}
return im;
}
我调用此函数来生成图像。为每个像素着色:
Vec Tracer::render(int i, int j)
{
Vec pxlcol(0,0,0);
Vec p(0,0,0);
int obj;
Vec dir = _scr.point(i,j);
dir = dir - _obs;
obj = intercept(_obs, dir, p);
if(obj != -1)
{
pxlcol = objCol(_obs, p, obj, _ref);
}
return pxlcol;
}
其中 int Tracer :: intercept(Vec o,Vec d,Vec&amp; p)是以下函数
int Tracer::intercept(Vec o, Vec d, Vec& p)
{
int obj, k;
Vec temp = o;
Vec ptry = o;
obj = -1;
for(k = 0; k < _nobj; k++)
{
temp = _obj[k]->intercept(o, d);
if( !(temp == o) && (((temp - o).mod() < (ptry - o).mod()) || (ptry == o)))
{
ptry = temp;
obj = k;
}
}
p = ptry;
return obj;
}
从 o 开始查找被ray d 拦截的对象的索引k,并返回 p 点,此类拦截发生,和 Vec Tracer :: objCol(Vec o,Vec p,int obj,int r)是函数
Vec Tracer::objCol(Vec o, Vec p, int obj, int r)
{
Vec colour(0,0,0);
Vec point(0,0,0), reflected(0,0,0);
unit ref = _obj[obj]->ref(p);
if((1 - ref) > 0)
point = pointCol(o, p, obj);
if(ref > 0 && r > 0)
reflected = refCol(o, p, _obj[obj]->normal(o, p), r - 1);
colour = (point*(1 - ref)) + (reflected*ref);
return colour;
}
找到 p 指向 o 方向发送的颜色,知道它属于对象 obj 并且最大反射次数允许 r 。
当我将背面(白色)墙反射时,结果就是这样:
看起来很好,那里没有问题。现在,如果我将正确的(蓝色)墙反射,结果就是:
(我还不能发布超过2个链接,删除括号以查看图像)。如果我决定让 sphere 反思,那就更糟了:
透明!而且我不知道为什么会发生这种情况,因为我在获取颜色之前会寻找光线拦截。
以下是我找到点颜色和反射颜色的代码:
Vec Tracer::pointCol(Vec o, Vec p, int obj)
{
Vec pcol(0,0,0);
Vec inten(0,0,0);
unit cos = 0;
Vec c(0,0,0);
Vec normal = _obj[obj]->normal(o, p);
Vec inter = o;
Vec objcol = _obj[obj]->colour(p);
bool block = false;
if(_amb == 1)
return objcol;
int l = 0;
int k = 0;
for(l = 0; l < _nlight; l++)
{
c = _li[l]->getPos() - p;
block = false;
if(c*normal > 0)
{
for(k = 0; k < _nobj; k++)
{
if(k != obj)
{
inter = _obj[k]->intercept(p, c);
inter = inter - p;
if(!(inter.null()) && (inter.mod() < c.mod()))
{
block = true;
k = _nobj;
}
}
}
if(!block)
{
cos = (c*normal)/(c.mod()*normal.mod());
inten = inten + _li[l]->getInt()*cos;
}
}
}
inten = inten.div(_totalinten);
pcol = objcol*_amb + objcol.multi(inten)*(1 - _amb);
return pcol;
}
常量_amb定义了环境光的多少光和漫射的多少。
函数 Vec Vec :: div(Vec v)返回向量的逐点除法(例如(a,b,c).div((x,y,z))=( a / x,b / y,c / z))。函数 Vec Vec :: multi(Vec v)对乘法也是一样的。
Vec Tracer::refCol(Vec o, Vec p, Vec n, int r)
{
Vec rcol(0,0,0);
Vec i = p - o;
Vec refl(0,0,0);
int obj = 0;
Vec point(0,0,0);
i.normalise();
refl = reflected(i, n);
obj = intercept(p, refl, point);
if(obj != -1)
{
rcol = objCol(p, point, obj, r);
}
return rcol;
}
由于 objCol 使用参数r-1调用 refCol ,因此反射次数上限为_ref。
知道什么可能导致这些奇怪的反射效果吗?
答案 0 :(得分:3)
这是一个评论,我做了一个答案,因为它似乎有效:
虽然我还没有检查过代码,但这看起来像是一个z-fighting /浮点比较问题 - 尝试沿着法线移动交叉点远离反射物体,或者确保反射光线可以' t与原始物体碰撞,这可以解决它。
这里最有可能发生的是随机光线被物体反射,但由于交点不准确而再次击中物体(浮点不准确) - 这将导致反射光线再次翻转并被反射更频繁或最终进入正确(反射)方向 - 或以错误(原始)方向穿过物体,看起来像物体是透明的。