У чому полягає корисність радіуса прямокутника та оберненого радіуса квадрата для обчислень освітлення?


16

На одному із слайдів PowerPoint "DirectX 11 Rendering in Battlefield 3" я помітив наступний код:

struct Light {
    float3 pos; float sqrRadius;
    float3 color; float invSqrRadius;
}

Я не розумію, чому б вони зберігали радіус квадрата і навіть зворотний квадрат (який я вважаю просто радіусом 1 квадрата), а не просто зберігати радіус? Як вони використовують ці дані у своїх розрахунках? Більше того, що з конусними та лінійними фарами? Ця структура повинна бути тільки для точкових вогнів, я не можу бачити, як вона працює для інших типів - немає обґрунтованих даних. Ще я хотів би знати, як вони використовують цю площу та invSquare.

ОНОВЛЕННЯ: Добре, я нарешті зрозумів.

Ось класичне рівняння загасання світла, яке легко знайти в мережі:

float3 lightVector = lightPosition - surfacePosition;

float attenuation = saturate(1 - length(lightVector)/lightRadius);

Це порівняно дорого, як length(lightVector)це відбувається насправді:

length(lightVector) = sqrt(dot(lightVector, lightVector);

до того ж, операція відділення (/lightRadius)також досить дорога.

Замість обчислення ослаблення світла таким чином, ви можете обчислити його наступним чином, що було б набагато швидше:

attenuation = saturate(1 - dot(lightVector, lightVector)*invRadiusSqr);

де invRadiusSqr можна заздалегідь обчислити на рівні процесора і передати як константу шейдера.

Більше того, ви отримуєте квадратичне загасання світла в результаті (замість лінійного в колишньому випадку), що ще краще, оскільки світло ІРЛ показав квадратичне випадання.

Дякую всім за допомогу!


13
Прокладки. Шейдери вирівняні на 16 байт. І якщо ви подивитеся на те, як формується код, ви побачите, що він буде упакований у два float4. Чому б не зберігати щось корисне, коли ви отримаєте його безкоштовно з кеша?
Тордін

Відповіді:


23

Це просто своєрідна оптимізація, враховуючи те invSqrRadius = 1/SqrRadius, що замість обчислення радіуса оберненого квадрата для кожного світла кожного разу, коли вони просто кешують його, причина полягає в тому, що поділ, як правило, є "повільною" операцією принаймні в порівнянні з множенням.

Ця оптимізація особливо актуальна:

  • коли операція виконується величезна кількість разів за кожне світло, тому кешування значення звільнить додаткові цикли процесора / GPU
  • І припускаючи, що час доступу до пам'яті для читання значення насправді швидше, ніж перерахунок його.

Щодо того, як він використовується, я не впевнений у їх конкретному виконанні, але щодо 1/sqrRadiusцього це просто використовується для ослаблення світла, випадання та випадання. Це також актуально для спрямованого та прожекторного світла. Єдина відмінність у випадку прожектора - це те, що вам потрібно обчислити коефіцієнт прожектора після застосування ослаблення . Що стосується спрямованих вогнів, таких як сонце, воно, як правило, не має ослаблення або випадання, тому я б припустив, що це буде ігноровано.

[EDIT] Просто для того, щоб розробити більше, це не є неактуальними даними. Опромінення світла можна обчислити, використовуючи наступне рівняння:

E = Phi / 4 * pi * rSqr;

Де

E - площа щільності потоку.

Phi - це випромінювання потоку.

4 * pi * rSqr - площа поверхні кулі.

Це рівняння пояснює, чому кількість отриманої енергії падає з відстані у квадраті.

Ще один момент - вам потрібно обчислити відстань між вершиною та світлом, щоб обчислити внесок світла на певну вершину (це значення навряд чи буде кешоване), вершина може знаходитись у діапазоні світла чи виходити з нього, що приводить нас до наступної точки де Radius Squareкорисно для вибивання.

Якщо ви хочете отримати практичний приклад для обчислення випадіння світла та відсікання, це особливо корисно для відкладених викладених плиткою плит, ось один приклад .


Прокляття, ви мене побили 7 секунд! ;) (і з більш вичерпною відповіддю теж!)
Тревор Пауелл

Дякуємо за детальний коментар, esp за посилання! З того, що я зрозумів з останнього посилання, Battlefield 3 зберігає не радіус, а фактичну відстань між джерелом світла та приймачем світла, правда? Це значення "d", яке вони використовують у статті.
Куберман

@cubrman важко спекулювати, не бачачи коду. Я здогадуюсь, що це зворотний радіусSqr. І рівняння, які вони використовують, можуть сильно відрізнятися від статті.
concept3d

Але скажіть, як ви можете використовувати голий перевернутий радіус світла в квадраті для розрахунку освітлення? Кожне джерело, яке я знайшов у мережі, говорить мені про те, що мені потрібно знайти РІЗНІСТЬ між приймальною поверхнею та джерелом світла, поділити останнє на початковий радіус світла ТА ТЕКУТИ, квадратний результат. Де б ви ніколи не використовували квадратний радіус або invSqrRadius? Мені це здається абсолютно невідповідними даними.
Куберман

1
@cubrman оновив відповідь.
concept3d

6

invSqrRadius не 1 - sqrRadius; це 1 / sqrRadius.

Це означає, що ви можете помножити на invSqrRadius, а не ділити на sqrRadius (оскільки ділення зазвичай набагато дорожче, ніж множення)


6

Інші відповіді тут стосувались зворотного радіуса квадрата, але я замість цього шукаю радіус квадрата (який концепція 3d торкнулася, але я вважаю, що це заслуговує на подальше обговорення).

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

Якщо sqrt (x)> sqrt (y), то так само x> y.

Для світла радіус квадрата такий самий, як відстань між центром світла, і це, звичайно, максимальна міра - квадрат.

Для обчислень освітлення це може бути використано для ранніх випадків. Якщо відстань між точкою, яку ви освітлюєте, і центром світла (у квадраті) більше радіуса квадрата, точка не отримує світла, і вам не потрібно виконувати решту ваших обчислень. Тому це лише оптимізація (досить поширена) - ми можемо використовувати радіус квадрата для порівняння відстані без дорогих квадратних коренів і ціною просто віднімання та крапкового добутку.

Я, звичайно, не знаю, чи саме для цього використовується BF3, але я би сподівався, що я не надто далеко від позначки.


Тож якщо я вас правильно зрозумів, код буде: якщо (крапка ((lightPos - поверхняPos), (lightPos - поверхняPos))> lightRadiusSqr) не займається освітленням, правда?
Куберман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.