Какая польза от квадрата радиуса и обратного квадрата радиуса для расчетов освещения?


16

На одном из слайдов «Рендеринг DirectX 11 в Battlefield 3» в PowerPoint я заметил следующий код:

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 может быть предварительно вычислен на уровне процессора и передан как константа шейдера.

Более того, в результате вы получаете квадратичное затухание света (вместо линейного в первом случае), что даже лучше, поскольку свет IRL показал квадратичный спад.

Спасибо всем за вашу помощь!


13
Перетяжка. Шейдеры выровнены по 16 байтов. И если вы посмотрите, как форматируется код, вы увидите, что он будет упакован в два float4. Почему бы не хранить что-то полезное, когда вы получите его бесплатно из кеша?
Тордин

Ответы:


23

Это просто своего рода оптимизация, учитывая, что invSqrRadius = 1/SqrRadiusвместо вычисления обратного квадрата радиуса для каждого источника света каждый раз, когда они просто кешируют его, причина в том, что деление обычно является «медленной» операцией, по крайней мере, по сравнению с умножением.

Эта оптимизация актуальна особенно:

  • когда операция выполняется огромное количество раз для каждого источника света, поэтому кэширование значения освободит дополнительные циклы CPU / GPU
  • И если предположить, что время доступа к памяти для чтения значения на самом деле быстрее, чем его пересчет.

Относительно того, как это используется, я не уверен относительно их конкретной реализации, но относительно того 1/sqrRadius, что это просто используется для ослабления света, спада и отбраковки. Это также актуально для направленного и точечного освещения, единственное различие в случае прожектора состоит в том, что вам необходимо вычислить коэффициент прожектора после применения затухания . Что касается направленного света, такого как солнце, оно обычно не имеет никакого затухания или спада, поэтому я думаю, оно будет проигнорировано.

[РЕДАКТИРОВАТЬ] Просто, чтобы уточнить, это не несущественные данные. Излучение света может быть рассчитано с использованием следующего уравнения:

E = Phi / 4 * pi * rSqr;

где

Е - площадь плотности потока.

Фи - это поток сияния.

4 * pi * rSqr - площадь поверхности сферы.

Это уравнение объясняет, почему количество получаемой энергии уменьшается с квадратом расстояния.

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

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


Проклятия, ты побил меня на 7 секунд! ;) (и с более полным ответом тоже!)
Тревор Пауэлл

Спасибо за подробный комментарий, особенно за ссылку! Из того, что я понял из последней ссылки, Battlefield 3 хранит не радиус, а фактическое расстояние между источником света и приемником света, верно? Это значение "d", которое они используют в статье.
Cubrman

@ cubrman трудно спекулировать, не видя код. Я предполагаю, что это обратный радиус. И уравнения, которые они используют, могут сильно отличаться от статьи.
concept3d

Но скажите, как вы можете использовать квадраты радиуса голого инвертированного света при расчете освещения? Каждый источник, который я нашел в сети, говорит мне, что мне нужно найти РАССТОЯНИЕ между принимающей поверхностью и источником света, разделить последнюю на первоначальный радиус света И ПОТОМ возвести в квадрат результат. Где бы вы использовали квадрат радиуса или invSqrRadius? Это кажется совершенно неактуальными данными для меня.
Cubrman

1
@cubrman обновил ответ.
concept3d

6

invSqrRadius не 1 - sqrRadius; это 1 / sqrRadius.

Это означает, что вы можете умножить на invSqrRadius вместо деления на sqrRadius (поскольку деление обычно намного дороже, чем умножение)


6

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

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

Если sqrt (x)> sqrt (y), то это также тот случай, когда x> y.

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

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

Я, конечно, не знаю, использует ли это именно BF3, но я ожидаю, что я не слишком далеко от цели.


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