光线跟踪反射伪像和透明度

时间:2014-02-11 14:53:46

标签: c++ raytracing

我一直在研究一个类的光线跟踪算法,并遇到了一个奇怪的问题。

img

这是我正在研究的基本场景。什么都不重要,一切都好。现在,我的代码就这样组织起来了:

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

当我将背面(白色)墙反射时,结果就是这样:

img

看起来很好,那里没有问题。现在,如果我将正确的(蓝色)墙反射,结果就是:

img

(我还不能发布超过2个链接,删除括号以查看图像)。如果我决定让 sphere 反思,那就更糟了:

img

透明!而且我不知道为什么会发生这种情况,因为我在获取颜色之前会寻找光线拦截。

以下是我找到点颜色和反射颜色的代码:

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。

知道什么可能导致这些奇怪的反射效果吗?

1 个答案:

答案 0 :(得分:3)

这是一个评论,我做了一个答案,因为它似乎有效:

虽然我还没有检查过代码,但这看起来像是一个z-fighting /浮点比较问题 - 尝试沿着法线移动交叉点远离反射物体,或者确保反射光线可以' t与原始物体碰撞,这可以解决它。

这里最有可能发生的是随机光线被物体反射,但由于交点不准确而再次击中物体(浮点不准确) - 这将导致反射光线再次翻转并被反射更频繁或最终进入正确(反射)方向 - 或以错误(原始)方向穿过物体,看起来像物体是透明的。