Подтвердить что ты не робот

Что вызывает артефакты в моем raytracer?

EDIT: я решил проблему; вы можете увидеть мое решение в ответах.

Я занимаюсь написанием raytracer в реальном времени с использованием OpenGL (в GLSL Compute Shader), и у меня возникла небольшая проблема с некоторыми из моих пересечений линий треугольника (или, по крайней мере, я считаю, что они виновник). Вот картина того, что происходит:

Spheres in a room with some artifacts

Как вы можете видеть, некоторые пиксели окрашиваются в черный цвет на пересечении двух треугольников вблизи верхней части изображения. Вероятно, это связано с тем, как я обрабатываю поплавки или что-то в этом роде, и я попытался найти решение в Интернете, но не могу найти похожие ситуации. Возможно, есть важное ключевое слово, которое мне не хватает?

В любом случае важной частью кода является этот:

#define EPSILON 0.001f
#define FAR_CLIP 10000.0f
float FindRayTriangleIntersection(Ray r, Triangle p)
{
    // Based on Moller-Trumbone paper
    vec3 E1 = p.v1 - p.v0;
    vec3 E2 = p.v2 - p.v0;
    vec3 T = r.origin - p.v0;
    vec3 D = r.dir;
    vec3 P = cross(D, E2);
    vec3 Q = cross(T, E1);

    float f = 1.0f / dot(P, E1);
    float t = f * dot(Q, E2);
    float u = f * dot(P, T);
    float v = f * dot(Q, D);

    if (u > -EPSILON && v > -EPSILON && u+v < 1.0f+EPSILON) return t;
    else return FAR_CLIP;
}

Я пробовал различные значения для EPSILON, пробовал варианты с +/- для значений EPSILON, но безрезультатно. Кроме того, изменение 1.0f+EPSILON на a 1.0-EPSILON дает устойчивую черную линию на всем пути.

Также, чтобы уточнить, определенно НЕ является пробелом между двумя треугольниками. Они плотно упакованы (и я также попытался расширить их, чтобы они пересекались, но я все равно получаю одни и те же черные точки).

Любопытно, что нижнее пересечение не показывает никаких признаков этого явления.

Последнее примечание: если требуется больше моего кода, просто спросите, и я попытаюсь выделить еще один код (или, возможно, просто ссылку на весь шейдер).

ОБНОВЛЕНИЕ. Было указано, что "черные артефакты" на самом деле коричневые. Поэтому я углубился и выключил все отражения, и получил этот результат:

Spheres without reflections

Коричневый цвет на самом деле происходит только из медного материала сверху, но, что более важно, я думаю, что у меня есть идея, в чем причина проблемы, но я не ближе к ее решению.

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

Итак, я полагаю, что теперь вопрос сводится к следующему: как я могу иметь некоторую согласованность при решении вопроса о том, какой треугольник следует использовать в таких случаях?

4b9b3361

Ответ 1

Итак, выяснилось, что это был не тот код, который я опубликовал, что вызвало эту проблему. Благодаря некоторой помощи в комментарии, я смог найти, что это был этот код, когда я определяю ближайший объект к камере:

float nearest_t = FAR_CLIP;
int nearest_index = 0;
for (int j=0; j<NumObjects; j++)
{
    float t = FAR_CLIP;
    t = FindRayObjectIntersection(r, objects[j]);

    if (t < nearest_t && t > EPSILON && t < FAR_CLIP)
    {
        nearest_t = t;
        nearest_index = j;
    }
}

При определении t иногда треугольники были настолько близки друг к другу, что t < nearest_t имел почти вероятностный результат, так как пересечения были примерно на одинаковом расстоянии от камеры.

Моим первоначальным решением было изменить внутренний оператор if на:

if (t < nearest_t-EPSILON && t > EPSILON && t < FAR_CLIP)

Это гарантирует, что если два пересечения очень близко друг к другу, он всегда будет выбирать первый объект для отображения (если только второй объект не ближе по меньшей мере к EPSILON). Вот результирующее изображение (с отключенными отражениями):

Spheres

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

if (t > EPSILON && t < FAR_CLIP && abs(nearest_t - t) < EPSILON)
{
    nearest_index2 = nearest_index;
    nearest_t2 = nearest_t;
}
if (t < nearest_t+EPSILON && t > EPSILON && t < FAR_CLIP)
{
    nearest_t = t;
    nearest_index = j;
}

Я также добавил этот код смешивания цветов:

OverallColor = mix(c1, c2, 0.5f * abs(T1 - T2) / EPSILON);

После этих двух шагов, и, честно говоря, я думаю, что этот эффект был больше из логического изменения, чем изменение смешивания, я получил этот результат:

Spheres

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

Happy raytracing

Счастливый трафик!