RayTracing — какая должна быть формула для корректного затенения поверхности под различными углами?

Добрый вечер. Делаю PathTracer и столкнулся с проблемой:

для расчета теней прямого освещения я получаю для каждого пикселя его освещенность по формуле:

return emissionColor // RGB(255, 255, 255)
                .scale(lightPower - rayLine.getLength() * (lightPower / fadeRadius)) // затухание с увеличением расстояния
                .scale(lambertCos); // 0 - 1


В данном коде:
  • lightPower - сила источника света (в примере - 2)
  • rayLine.getLength() длина луча от точки, для которой рассчитываем освещенность до случайной точки на источнике света (для рассеянных теней)
  • fadeRadius - расстояние с которого свет от источника света полностью затухает


lambertCos - рассчитывается по формуле
0 - Vector.dot( // 0  - специально, что бы было заметно отрицательное число
        lightDirection, // вектор из точки, для которой рассчитываем освещенность, к источнику света, <b>нормализованный</b>
        intersection.getNormal() - нормаль точки, для которой рассчитываем освещенность
 );


Возьмем сцену - куб, верхняя плоскость куба - источник света.

С вышеприведенными формулами куб освещается корректно.

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

0a5541a7ac.jpg

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

surfaceCost = Vector.dot(
        lightDirection,
        shadowRay.getNormal() // нормаль в точке пересечения луча в точке на источнике света
);

return emissionColor // RGB(255, 255, 255)
                .scale(lightPower - rayLine.getLength() * (lightPower / fadeRadius)) // затухание с увеличением расстояния
                .scale(lambertCos) // 0 - 1
                .scale(surfaceCos); // 0 - 1


Результат станет таким, каким нужно, будет выглядеть процентов на 90, как на этой картинке

cornellBox_matteAndGloss.png

На картинке видно, что если источник света не занимает всю поверхность потолка, то он дает легкое затенение на стенах под потолком.

Ну а теперь проблема:
растянем источник света вновь до размеров всего потолка - как результат затенение под источником света все равно останется, т.к. лучи в затененных участках по прежнему будут находиться пол сильным углом к нормали источника света.

Вот как это выглядит (слева сверху)
f7033290c1.jpg

Я пытался привязать surfaseCos к расстоянию между точкой на поверхности и точкой на источнике света:
surfaseCos = 1 - (surfaseCos * (rayline.getDist() * fadeRadius))


но почему то результат все равно не правильный. На последней картинке, теней на красной стене под ИС быть не должно, а на белой стене они должны появляться сначала резкими, затем становиться все более размытыми, как только ИС начинает удаляться от стены (сужаться), опускаться вниз.

Вообщем - есть у кого какие идеи по моей проблеме?
  • Вопрос задан
  • 360 просмотров
Решения вопроса 1
@sanex3339 Автор вопроса
Ну, вроде поправил.

ab53580fd6d8414d9e1f8cb916683422.jpg

Суть оказалась в том, что у меня итоговая картинка собирается из пассов (для каждого пикселя):
цвет поверхности, пасс - черно-белая маска прямого освещения, пасс прямого освещения (комбинированные 1-е 2 пасса), пасс ambient occlusion и пасс глобального освещения.

При расчете пасса глобального освещения у меня рекурсивно берутся данные глобального освещения за следующие итерации.

И тут крылся баг: если точка, для которой рассчитывалось освещение являлась источником света - я просто возвращал цвет источника (emission color), а надо было еще назначить в сооветствующие переменные пассов - необходимые цвета, т.к. данные переменных каждого пасса используются в предыдущих итерациях:

this.surfaceColor = intersection.getOwner().getMaterial().getEmission().getEmissionColor();
            this.lightSamplingColor = RGBColor.WHITE;
            this.ambientOcclusionColor = this.lightSamplingColor;
            this.globalIlluminationColor = this.surfaceColor;

            return this.surfaceColor;


Вот получившиеся рендеры с одинаковыми настройками:

417d1bd62d0640f6ad79a884c127536f.jpg1c385c83ffbc423f8baa749ba8d3a1de.jpg

Теперь бы еще понять - что за дурацкие разводы на сферах, которые имеют место с самых первых версий PathTracer'а.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы