Я пишу базовый трассировщик лучей, чтобы лучше понять все это. Я столкнулся с проблемой, которая сдерживала меня некоторое время, Диффузное затенение сферы. Я использовал формулу из следующего источника для расчета пересечений сферы и диффузного затенения.
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);
Во-первых, если ваш 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);