Простой трассировщик лучей, проблемы с диффузным затенением С++

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

http://www.ccs.neu.edu/home/fell/CSU540/programs/RayTracingFormulas.htm

Мой код, который вычисляет затенение (попытка репликации исходного кода по ссылке), показан выше. По большей части вычисления кажутся правильными для некоторых сфер, время от времени, однако в зависимости от положения источников света зависит, насколько правильным/неправильным выглядит затенение сфер.

TVector intersect   (ray.getRayOrigin().getVectX() + t * (ray.getRayDirection().getVectX() - ray.getRayOrigin().getVectX()),
                    ray.getRayOrigin().getVectY() + t * (ray.getRayDirection().getVectY() - ray.getRayOrigin().getVectY()),
                    ray.getRayOrigin().getVectZ() + t * (ray.getRayDirection().getVectZ() - ray.getRayOrigin().getVectZ()));

//Calculate the normal at the intersect point
TVector NormalIntersect (intersect.getVectX() - (position.getVectX()/r), 
                        intersect.getVectY() - (position.getVectY()/r),
                        intersect.getVectZ() - (position.getVectZ()/r));

NormalIntersect = NormalIntersect.normalize();

//Find unit vector from intersect(x,y,z) to the light(x,y,z)
TVector L1 (light.GetPosition().getVectX() - intersect.getVectX(), 
            light.GetPosition().getVectY() - intersect.getVectY(),
            light.GetPosition().getVectZ() - intersect.getVectZ());
L1 = L1.normalize();
double Magnitude = L1.magnitude();

TVector UnitVector(L1.getVectX() / Magnitude,
                   L1.getVectY() / Magnitude,
                   L1.getVectZ() / Magnitude);

//Normalized or not, the result is the same
UnitVector = UnitVector.normalize();

float Factor = (NormalIntersect.dotProduct(UnitVector));
float kd = 0.9;             //diffuse-coefficient
float ka = 0.1;             //Ambient-coefficient
Color pixelFinalColor(kd * Factor * (color.getcolorRed())  +  (ka * color.getcolorRed()) ,
                      kd * Factor * (color.getcolorGreen())  + (ka * color.getcolorGreen())  ,
                      kd * Factor * (color.getcolorBlue()) +  (ka * color.getcolorBlue()) ,1);

Ошибка диффузного затенения

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

Примечание. Я использую OpenGl для рендеринга сцены.

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

TVector UnitVector (light.GetPosition().getVectX() - intersect.getVectX(), 
                    light.GetPosition().getVectY() - intersect.getVectY(),
                    light.GetPosition().getVectZ() - intersect.getVectZ());

UnitVector = UnitVector.normalize();
float Factor = NormalIntersect.dotProduct(UnitVector);

//Set Pixel Final Color
Color pixelFinalColor(min(1,kd * Factor * color.getcolorRed())  +  (ka * color.getcolorRed()) ,
                      min(1,kd * Factor * color.getcolorGreen())  + (ka * color.getcolorGreen())  ,
                      min(1,kd * Factor * color.getcolorBlue()) +  (ka * color.getcolorBlue()) ,1);

person Unknown    schedule 26.12.2013    source источник
comment
Убедитесь, что окончательный цвет не выходит за пределы цветового диапазона (от 0 до 1 или 255). В зависимости от библиотеки слишком яркие цвета могут накладываться и становиться черными. Вставьте мин.: pixelFinalColor(min(1, kd * Factor... Сделайте то же самое для Factor = max(0, NormalIntersect...   -  person Nico Schertler    schedule 26.12.2013
comment
Спасибо, хороший совет. После добавления min(1) к цвету пикселя кажется, что проблема устранена на некоторых сферах, однако у других в разных позициях возникла та же проблема. Если я также добавлю max(0) к фактору, все объекты станут плоско закрашенными одним тоном, без диффузного затенения вообще. Все равно ценю вашу помощь. Спасибо.   -  person Unknown    schedule 26.12.2013
comment
Если все выглядит одним тоном, вероятно, он не освещен и просто показывает ваше окружающее освещение. Вы уверены, что ваш свет там, где вы думаете?   -  person timday    schedule 26.12.2013
comment
Положительный, до добавления max(0) В зависимости от того, куда я перемещаю свет (x, y, z), меняется способ затенения сфер, и для тех, которые не сломаны, он выглядит точным. Я протестировал несколько положений освещения, и это определенно не проблема. спасибо за ваше время и вклад.   -  person Unknown    schedule 26.12.2013


Ответы (2)


Во-первых, если ваш getRayDirection() делает то, что он говорит, он создает направление, а не точку взгляда, поэтому вам не следует вычитать из него начало луча, которое является точкой. (Хотя направления и точки представлены векторами, нет смысла добавлять точку к направлению)

Кроме того, вы нормализуете L1, а затем берете его величину и делите каждый из ее компонентов на эту величину, чтобы получить UnitVector, который вы затем вызываете normalize снова. В этом нет необходимости: величина L1 после первой нормализации равна 1, вы нормализуете один и тот же вектор 3 раза, просто используйте L1.

Последняя проблема, это проблема зажима. Переменная, которую вы называете Factor, представляет собой значение cos(th), где th — это угол между направлением света и нормалью. Но cos(th) имеет диапазон [1,-1], а вам нужен диапазон только [0,1], поэтому вы должны зафиксировать Factor в этом диапазоне:

Factor = max(0, min( NormalIntersect.dotProduct(UnitVector), 1));

(И уберите min вызовов в своем производстве color).

Это ограничение необходимо для граней, нормали которых обращены от источника света, которые будут иметь отрицательные значения cos(th). Угол между их нормалью и направлением света больше pi/2. Интуитивно они должны казаться максимально темными по отношению к рассматриваемому свету, поэтому мы фиксируем их на 0).

Вот моя окончательная версия кода, которая должна работать. Я буду исходить из предположения, что в вашем классе TVector определены scale(float) и operator +(const TVector &) и т. д., потому что они явно облегчат вам жизнь. Также для краткости я буду называть NormalIntersect, просто normal:

TVector 
    intersect = ray.getRayOrigin() + ray.getRayDirection().scale(t),
    normal    = (intersect - position).normalize(),
    L1        = (light.GetPosition() - intersect).normalize();

float
    diffuse   = max(0, min(normal.dotProduct(L1), 1)),
    kd        = 0.1,
    ka        = 0.9;

Color pixelFinalColor = color.scale(kd * diffuse + ka);
person amnn    schedule 27.12.2013
comment
Отличный ответ, спасибо за ваше время. Это исправило мои проблемы. Что касается нормализации L1 слишком много раз, я заметил, что прошлой ночью это была довольно глупая ошибка. Еще раз спасибо. - person Unknown; 27.12.2013

вы неправильно расставили фигурные скобки в обычном расчете, это должно быть

TVector NormalIntersect ((intersect.getVectX() - position.getVectX())/r, 
                         (intersect.getVectY() - position.getVectY())/r,
                         (intersect.getVectZ() - position.getVectZ())/r);
person Vasaka    schedule 26.12.2013
comment
Я обновил это, извините, в коде могут быть небольшие ошибки, когда я пытался это исправить, однако это исправление не повлияло на конечный результат. Большое спасибо за ваш ответ - person Unknown; 26.12.2013
comment
это не является незначительной ошибкой и должно сильно сломать ситуацию, если только ваш радиус не близок к 1 - person Vasaka; 26.12.2013
comment
Извините, я понимаю вашу точку зрения, я сказал только незначительный, потому что это было что-то, что ранее было правильным, просто неуместным в коде, который я разместил здесь. Большое спасибо за ваш ответ. С этим исправлением у вас есть какие-либо идеи относительно того, почему я получаю эти ошибки затенения? Спасибо - person Unknown; 26.12.2013