Простой Ray Tracer, проблемы диффузного затенения с ++

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

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);

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).

Вот мой окончательный вариант кода, который должен работать. Я собираюсь работать в предположении, что у вас есть scale(float) а также operator +(const TVector &) и т. д. определено на вашем 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);

Вы неуместные брекеты в обычном расчете это должно быть

TVector NormalIntersect ((intersect.getVectX() - position.getVectX())/r, 
                         (intersect.getVectY() - position.getVectY())/r,
                         (intersect.getVectZ() - position.getVectZ())/r);
Другие вопросы по тегам