Что вызывает артефакты в моем 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]()
Коричневый цвет на самом деле происходит только из медного материала сверху, но, что более важно, я думаю, что у меня есть идея, в чем причина проблемы, но я не ближе к ее решению.
Похоже, что когда лучи испускаются из-за очень слабых несовершенств в плавающей арифметике, некоторые лучи пересекают верхний треугольник, а некоторые пересекают нижнюю часть.
Итак, я полагаю, что теперь вопрос сводится к следующему: как я могу иметь некоторую согласованность при решении вопроса о том, какой треугольник следует использовать в таких случаях?
Ответы
Ответ 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]()
Счастливый трафик!