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



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



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



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



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



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



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



вот предварительный просмотр моего нынешнего реализация:



area lighting teaser



введение



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




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

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

  3. вычислите 2D смещение этой проецируемой точки на плоскости от центра/положения света.

  4. зажмите этот вектор смещения 2D так, чтобы он находился внутри области света (определяемой его шириной и высотой).

  5. выведите 3D мировое положение проецируемой и зажатой 2D точки. Это и есть ближайший пункт на площади света до вершины.

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


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



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



problematic area lighting situation



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



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



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



помогите!



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



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




  • вершина должность

  • вершинная Нормаль (единичный вектор)

  • светлое положение, ширина и высота

  • свет нормальный (единичный вектор)

  • свет справа (блок вектор)

  • загорается (единичный вектор)

  • проецируемая точка из вершины на плоскость освещения (3D)

  • смещение проекционной точки от центра огней (2D)

  • зажатое смещение (2D)

  • мировое положение этого зажатого смещения-то ближайший пункт (3D)


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



available lighting information



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



обновление!!!



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



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



codepen



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



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



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

826   5  

5 ответов:

хорошая новость заключается в том, что есть решение; но сначала плохие новости.

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

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

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

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

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

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

Еще Одна Хорошая Новость:

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

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

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

три.Яш Р.73

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

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

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

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

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

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

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

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

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

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

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

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

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..бесконечность

L = sum(k/(y^2+x0^2)), y=0..бесконечность, x > 0, y > 0

L = Интеграл (k / (y^2+x0^2)), y=0..бесконечность= k * Pi / (2*x0)

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

ответ:

L = k * Pi/(2*x0)

k зависит от окружающей среды

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

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

Comments

    Ничего не найдено.