如何处理离散光线跟踪的错误索引计算?

时间:2018-03-10 22:09:49

标签: opengl glsl textures raytracing

情况如下。我试图在glsl着色器中实现线性体素搜索,以实现高效的体素光线跟踪。在toehr的话,我有一个3D纹理,我在它上面进行光线追踪,但我试图光线追踪,这样我只能检查一次与光线相交的体素。

为此,我编写了一个程序,其中包含以下结果:

效率不高但是正确:

enter image description here

通过多次添加小ε射线并在每次迭代中从纹理采样来获得上述图像。这会产生正确的结果,但效率非常低。

这看起来像是:

loop{
     start += direction*0.01;
     sample(start);
}

为了提高效率,我决定改为实现以下查找功能:

float bound(float val)
{
    if(val >= 0)
        return voxel_size;
    return 0;
}

float planeIntersection(vec3 ray, vec3 origin, vec3 n, vec3 q)
{
    n = normalize(n);
    if(dot(ray,n)!=0)
        return (dot(q,n)-dot(n,origin))/dot(ray,n);

    return -1;
}

vec3 get_voxel(vec3 start, vec3 direction)
{
    direction = normalize(direction);

    vec3 discretized_pos = ivec3((start*1.f/(voxel_size))) * voxel_size;

    vec3 n_x = vec3(sign(direction.x), 0,0);
    vec3 n_y = vec3(0, sign(direction.y),0);    
    vec3 n_z = vec3(0, 0,sign(direction.z));

    float bound_x, bound_y, bound_z;

    bound_x = bound(direction.x);
    bound_y = bound(direction.y);
    bound_z = bound(direction.z);

    float t_x, t_y, t_z;

    t_x = planeIntersection(direction, start, n_x, 
        discretized_pos+vec3(bound_x,0,0));

    t_y = planeIntersection(direction, start, n_y, 
        discretized_pos+vec3(0,bound_y,0));

    t_z = planeIntersection(direction, start, n_z, 
        discretized_pos+vec3(0,0,bound_z));

    if(t_x < 0)
        t_x = 1.f/0.f;
    if(t_y < 0)
        t_y = 1.f/0.f;
    if(t_z < 0)
        t_z = 1.f/0.f;

    float t = min(t_x, t_y);
    t = min(t, t_z);

    return start + direction*t;
}

产生以下结果:

enter image description here

注意某些表面左侧的三角形别名。

看起来这种混叠是因为某些坐标没有设置为正确的体素。

例如,修改截断部分如下:

vec3 discretized_pos = ivec3((start*1.f/(voxel_size)) - vec3(0.1)) * voxel_size;

创建:

enter image description here

因此它解决了某些表面的问题,并将其引入其他表面。

我想知道是否有一种方法可以纠正此截断,以便不会发生此错误。

更新

我已经缩小了这个问题。请注意以下图像:

enter image description here

数字表示我希望访问这些方框的顺序。

正如你可以看到的那样,第五个方框的抽样似乎被省略了。

以下是抽样代码:

vec4 grabVoxel(vec3 pos)
{

    pos *= 1.f/base_voxel_size;

    pos.x /= (width-1);
    pos.y /= (depth-1);
    pos.z /= (height-1);
    vec4 voxelVal = texture(voxel_map, pos);

    return voxelVal;
}

1 个答案:

答案 0 :(得分:3)

是的,这是+/-四舍五入,我在你之前关于此问题的某些地方的评论中谈到了这个问题。您需要做的是在其中一个轴上使用等于网格大小的步长(对|dx|=1测试3次,然后对|dy|=1测试3次,最后|dz|=1)

此外,您应该在地图中创建调试绘制 2D 切片,以实际查看单个特定测试光线的命中位置。现在根据每个轴的光线方向,分别设置舍入规则。如果没有这个,你只是盲目修补一个案子并腐蚀其他两个......

现在实际看看这个(我之前把它链接到了你,但你显然没有):

特别注意:

img

在右侧它显示了如何计算射线步骤(你的epsilon)。您只需缩放光线方向,使其中一个坐标为+/-1。为简单起见,请从地图开始使用2D切片。红点是射线起始位置。绿色是垂直网格线命中的光线步长向量,红色用于水平网格线命中(z将类似地相同)。

现在,您应该通过一些可见的高度切片添加地图的2D概览(如左图所示)为检测到的每个交叉点添加点或标记,但按颜色区分x,y和z命中。仅针对单个光线执行此操作(我使用视图光线的中心)。查看X+方向而不是X-时的拳头手柄视图,完成后移至Y,Z ...

在我的GLSL volumetric 3D back raytracer中,在查看这些内容之前,我还链接了您:

if (dir.x<0.0) { p+=dir*(((floor(p.x*n)-_zero)*_n)-ray_pos.x)/dir.x; nnor=vec3(+1.0,0.0,0.0); }
if (dir.x>0.0) { p+=dir*((( ceil(p.x*n)+_zero)*_n)-ray_pos.x)/dir.x; nnor=vec3(-1.0,0.0,0.0); }

if (dir.y<0.0) { p+=dir*(((floor(p.y*n)-_zero)*_n)-ray_pos.y)/dir.y; nnor=vec3(0.0,+1.0,0.0); }
if (dir.y>0.0) { p+=dir*((( ceil(p.y*n)+_zero)*_n)-ray_pos.y)/dir.y; nnor=vec3(0.0,-1.0,0.0); }

if (dir.z<0.0) { p+=dir*(((floor(p.z*n)-_zero)*_n)-ray_pos.z)/dir.z; nnor=vec3(0.0,0.0,+1.0); }
if (dir.z>0.0) { p+=dir*((( ceil(p.z*n)+_zero)*_n)-ray_pos.z)/dir.z; nnor=vec3(0.0,0.0,-1.0); }

他们就是我这样做的。如您所见,我对6个案例中的每一个使用不同的舍入/地板规则。这样你就可以处理案件而不会破坏另一个案件。舍入规则取决于许多内容,例如您的坐标系如何与(0,0,0)相关联,因此代码可能会有所不同,但if条件应该相同。另外正如您所看到的那样,我通过稍微偏移光线开始位置而不是在光线遍历循环castray内具有这些条件来处理此问题。

那个宏投射光线并寻找与网格的交点,并在其上面实际上对交叉点进行分类并使用第一个有效的交叉点(这是l,ll所针对的,并且不需要其他条件或光线结果的组合)。所以我处理这个问题的方法是为每个类型的交叉点(xyz)投射射线,从与同一轴的网格的第一个交叉点开始。您需要考虑起始偏移量,因此l,ll类似于到实际射线开始的交叉点距离,而不是偏离一个...

另外一个好主意是首先在 CPU 方面执行此操作,而在 GLSL 中100%正常工作端口 GLSL 非常困难调试这样的事情。