Ситуация выглядит следующим образом. Я пытаюсь реализовать линейный поиск вокселей в шейдере glsl для эффективной трассировки воксельных лучей. Проще говоря, у меня есть 3D-текстура, и я использую трассировку лучей, но я пытаюсь трассировать лучи так, чтобы проверять только воксели, пересекаемые лучом один раз.

Для этого я написал программу со следующими результатами:

Не эффективно, но правильно:

enter image description here

Приведенное выше изображение было получено путем многократного добавления небольшого луча epsilon и выборки из текстуры на каждой итерации. Это дает правильные результаты, но очень неэффективно.

Это будет выглядеть так:

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;
}
3
Makogan 11 Мар 2018 в 01:09

1 ответ

Лучший ответ

Да, это было округление +/-, о котором я говорил в своих комментариях где-то в ваших предыдущих вопросах, связанных с этим. Что вам нужно сделать, так это иметь шаг, равный размеру сетки на одной из осей (и протестировать 3 раза один раз для |dx|=1, затем для |dy|=1 и, наконец, |dz|=1).

Также вы должны создать 2D срез карты отладки, чтобы увидеть, где действительно произошли попадания для одного конкретного тестового луча. Теперь в зависимости от направления луча на каждой оси вы устанавливаете правила округления отдельно. Без этого вы просто слепо исправляете один случай и портите два других ...

Теперь на самом деле посмотрите на это (я связывал это с вашим раньше, но вы явно этого не сделали):

Особенно обратите внимание на:

img

Справа показано, как вычислить шаг луча (ваш эпсилон). Вы просто масштабируете направление луча так, чтобы одна из координат была +/-1. Для простоты начните с двухмерного разреза вашей карты. Красная точка - это начальная позиция луча. Зеленый - вектор шага луча для попаданий вертикальных линий сетки, а красный - для попаданий горизонтальных линий сетки (z будет аналогичным образом).

Теперь вы должны добавить двухмерный обзор вашей карты через некоторый видимый срез высоты (как на изображении слева), добавить точку или маркер к каждому обнаруженному пересечению, но различать точки x, y и z по цвету. Сделайте это только для одиночного луча (я использую луч центра обзора). Вид рукоятки кулака, когда вы смотрите на X+ направления, чем на X-, а когда закончите, переходите к Y,Z ...

В моем трехмерном обратном трассировщике лучей GLSL я также связал вас, прежде чем взглянуть на эти строки:

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.

Этот макрос отбрасывает луч и ищет пересечения с сеткой, а поверх этого фактически выполняет z-сортировку пересечений и использует первое действительное (это то, для чего предназначен l,ll, и никаких других условий или комбинаций результатов луча не требуется). Так что мой способ справиться с этим - это луч лучей для каждого типа пересечения (x, y, z), начиная с первого пересечения с сеткой для той же оси. Вам нужно принять во внимание начальное смещение, чтобы l,ll напоминало расстояние пересечения от реального начала луча до смещения ...

Также неплохо сначала сделать это на стороне ЦП , и когда 100% рабочий порт на GLSL , как в GLSL , очень сложно отлаживать. как это.

3
Spektre 11 Мар 2018 в 14:23