Подтвердить что ты не робот

Улучшенное освещение зоны в WebGL & ThreeJS

Я работаю над реализацией области освещения в WebGL, подобной этой демонстрации:

http://threejs.org/examples/webgldeferred_arealights.html

Вышеупомянутая реализация в three.js была перенесена из работы ArKano22 поверх gamedev.net:

http://www.gamedev.net/topic/552315-glsl-area-light-implementation/

Хотя эти решения очень впечатляют, у них обоих есть несколько ограничений. Основная проблема с оригинальной реализацией ArKano22 заключается в том, что расчет диффузного члена не учитывает нормалей поверхности.

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

Вот предварительный просмотр моей текущей реализации:

area lighting teaser

Введение

Шаги для расчета диффузного члена для каждого фрагмента следующие:

  • Проецируйте вершину на плоскость, на которой светит площадь, так что проецируемый вектор совпадает с нормальным/направленным светом.
  • Убедитесь, что вершина находится на правильной стороне световой плоскости области, сравнивая вектор проекции с нормальным светом.
  • Вычислите двумерное смещение этой проецируемой точки на плоскости из центра света/положения.
  • Закрепите этот 2D-вектор смещения так, чтобы он находился внутри области света (определяемой его шириной и высотой).
  • Вывести трехмерное мировое положение проецируемой и зажатой двумерной точки. Это ближайшая точка на ярлыке области до вершины.
  • Выполните обычные диффузные вычисления, которые вы хотели бы для точечного света, взяв точечный продукт между вектором вершин к ближайшей точке (нормализованным) и нормальной вершиной.

Проблема

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

Рассмотрим следующую диаграмму:

problematic area lighting situation

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

Потенциальное решение

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

Помогите!

Итак, вот где мне нужна ваша помощь. В моей голове у меня довольно хорошее представление о том, как этот момент может быть получен, но у него нет математической компетентности для решения.

В настоящее время у меня есть следующая информация в моем шейдере фрагмента:

  • позиция вершины
  • vertex normal (единичный вектор)
  • светлое положение, ширина и высота
  • светлый нормальный (единичный вектор)
  • light right (единичный вектор)
  • загорается (единичный вектор)
  • проецируемая точка из вершины на плоскость света (3D)
  • прогнозируемое смещение точки от центра освещения (2D)
  • зажатое смещение (2D)
  • мировое положение этого зажатого смещения - ближайшая точка (3D)

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

available lighting information

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

ОБНОВЛЕНИЕ!!!

Я создал интерактивный эскиз на CodePen, который визуализирует математику, которую я в настоящее время реализовал:

http://codepen.io/wagerfield/pen/ywqCp

codepen

Относительным кодом, на который следует сосредоточиться, является строка 318.

castingPoint.location является экземпляром THREE.Vector3 и является недостающим фрагментом головоломки. Вы также должны заметить, что в левом нижнем углу эскиза есть 2 значения - они динамически обновляются, чтобы отображать точечный продукт между соответствующими векторами.

Я предполагаю, что для решения потребуется другая псевдоплоскость, которая выравнивается с направлением вершинной нормали И перпендикулярна плоскости света, но я мог ошибаться!

4b9b3361

Ответ 1

Хорошей новостью является решение; но сначала плохие новости.

Ваш подход к использованию точки, которая максимизирует точечный продукт, в корне ошибочна и не является физически правдоподобной.

В вашей первой иллюстрации выше предположим, что ваш свет области состоял только из левой половины.

"Фиолетовая" точка - та, которая максимизирует точечный продукт для левой половины, - такая же, как точка, которая максимизирует точечный продукт для обеих половинок, объединенных.

Следовательно, если бы вы использовали свое предлагаемое решение, можно было бы сделать вывод, что левая половина области света излучает такое же излучение, как и весь свет. Очевидно, что это невозможно.

Решение для вычисления общего количества света, которое излучает свет в данной точке, довольно сложно, но для справки вы можете найти объяснение в статье 1994 года. Якобиан Irradiance для частично окклюдированных многогранных источников .

Я предлагаю вам посмотреть Рисунок 1 и несколько абзацев Раздел 1.2 - и затем остановить.: -)

Чтобы сделать это легко, я закодировал очень простой шейдер, который реализует решение, используя three.js WebGLRenderer - не отложенный.

EDIT: здесь обновленная скрипка: http://jsfiddle.net/hh74z2ft/1/

enter image description here

Ядро шейдера фрагмента довольно просто

// direction vectors from point to area light corners

for( int i = 0; i < NVERTS; i ++ ) {

    lPosition[ i ] = viewMatrix * lightMatrixWorld * vec4( lightverts[ i ], 1.0 ); // in camera space

    lVector[ i ] = normalize( lPosition[ i ].xyz + vViewPosition.xyz ); // dir from vertex to areaLight

}

// vector irradiance at point

vec3 lightVec = vec3( 0.0 );

for( int i = 0; i < NVERTS; i ++ ) {

    vec3 v0 = lVector[ i ];
    vec3 v1 = lVector[ int( mod( float( i + 1 ), float( NVERTS ) ) ) ]; // ugh...

    lightVec += acos( dot( v0, v1 ) ) * normalize( cross( v0, v1 ) );

}

// irradiance factor at point

float factor = max( dot( lightVec, normal ), 0.0 ) / ( 2.0 * 3.14159265 );

Более хорошие новости:

  • Этот подход физически корректен.
  • Затухание обрабатывается автоматически. (Имейте в виду, что меньшие огни потребуют большего значения интенсивности.)
  • Теоретически этот подход должен работать с произвольными многоугольниками, а не только с прямоугольными.

Предостережения:

  • Я только реализовал диффузный компонент, потому что это ваш вопрос.
  • Вам нужно будет реализовать зеркальный компонент с использованием разумной эвристики - похоже на то, что вы уже закодировали, я ожидаю.
  • Этот простой пример не обрабатывает случай, когда свет области "частично под горизонтом" - то есть не все 4 вершины находятся над плоскостью лица.
  • Так как WebGLRenderer не поддерживает огни области, вы не можете "добавить свет в сцену" и ожидать, что он будет работать. Вот почему я передаю все необходимые данные в пользовательский шейдер. (WebGLDeferredRenderer действительно поддерживает подсветку зоны).
  • Тени не поддерживаются.

three.js r.73

Ответ 2

Hm. Нечетный вопрос! Похоже, вы начали с очень специфического приближения и теперь переходите к правильному решению.

Если мы придерживаемся только диффузного и плоской поверхности (имеет только один нормальный), что такое входящий диффузный свет? Даже если мы придерживаемся каждого входящего света, имеем направление и интенсивность, и мы просто принимаем allin = integ (lightin) ((lightin). (Normal)) * свет это сложно. поэтому вся проблема заключается в решении этого интеграла. с точечным светом, который вы обманываете, делая его суммой и вытягивая свет. Это прекрасно подходит для точечных огней без теней и т.д. Теперь то, что вы действительно хотите сделать, - это решить этот интеграл. что вы можете делать с помощью каких-то световых зондов, сферических гармоник или многих других методов. или некоторые трюки, чтобы оценить количество света от прямоугольника.

Для меня всегда полезно думать о полушарии выше точки, которую вы хотите осветить. Вам нужен весь свет. Некоторые из них менее важны, еще кое-что. Это то, что для тебя нормально. В производстве raytracer вы можете просто пробовать несколько тысяч очков и иметь хорошее предположение. В реальном времени вы должны угадывать намного быстрее. И это то, что делает ваш код библиотеки: быстрый выбор для хорошей (но ошибочной) догадки.

И что, когда я думаю, что вы идете в обратном направлении: вы поняли, что они делают предположение, и что это иногда отстой (что характер угадывания). Теперь не пытайтесь исправить их догадки, но придумайте лучший! И, возможно, попытайтесь понять, почему они выбрали это предположение. Хорошее приближение заключается не в том, чтобы быть хорошим в угловых случаях, но и в унизительной форме. Это то, что мне кажется. (Опять же, извините, я должен лениться читать код three.js сейчас).

Итак, чтобы ответить на ваш вопрос:

  • Я думаю, что вы делаете это неправильно. Вы начинаете с очень оптимизированной идеи и пытаетесь это исправить. Лучше начать с проблемы.
  • Решите одну вещь за раз. У вашего скриншота много зеркального, что не связано с вашей проблемой, но очень визуально и, вероятно, большое влияние на людей, разрабатывающих модель.
  • Вы на правильном пути и имеете гораздо лучшую идею об рендеринге, чем большинство людей. Это может работать и против вас. Прочитайте некоторые современные игровые движки и их модели освещения. Вы всегда найдете увлекательное сочетание хаков и глубокого понимания. Глубокое понимание - это то, что побуждает выбирать правильные хаки:)

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

Ответ 3

Согласитесь, что точка каста всегда находится на краю.

Скажем, что "освещенная часть" - это часть пространства, представленная экструдированным светом quad вдоль его нормали.

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

Если точка не находится в освещенной части, вы можете вычислить четыре плоскости, каждая из которых имеет точку поверхности, ее нормальную и одну из вершин светлого квадрата. Для каждой вершины света-квадрата у вас будет две точки (вершина + еще одна точка пересечения) для проверки, которая вносит наибольший вклад.

Это должно сделать трюк. Пожалуйста, дайте мне отзыв, если вы встретите какой-либо контрпример.

Ответ 4

http://s3.hostingkartinok.com/uploads/images/2013/06/9bc396b71e64b635ea97725be8719e79.png

Если я правильно понимаю:

определить L "Свет для точки x0"

L ~ K/S ^ 2

S = sqrt (y ^ 2 + x0 ^ 2)

L = sum (k/(sqrt (y ^ 2 + x0 ^ 2)) ^ 2), y = 0..infinity

L = sum (k/(y ^ 2 + x0 ^ 2)), y = 0..infinity, x > 0, y > 0

L = интеграл (k/(y ^ 2 + x0 ^ 2)), y = 0..infinity = k * Pi/(2 * x0)

http://s5.hostingkartinok.com/uploads/images/2013/06/6dbb7b6d3babc092d3daf18bb3c6e6d5.png

Ответ:

L = k * Pi/(2 * x0)

k зависит от среды

Ответ 5

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

http://gpupro.blogspot.com/2014/03/gpu-pro-5-physically-based-area-lights.html