Я знаю об объемном тумане, хотя никогда не видел его в реальной жизни. Две недели назад я впервые увидел Unreal, и эффекты тумана произвели на меня впечатление. Затем я увидел скриншоты выходящего скоро в свет Quake III: Arena. С тех пор я хочу создать объемный туман. У меня не было ни малейшей идеи к тому, как создать его в портальном/клеточном мире (portal/cell world). До тех пор, пока я не записал уравнение тумана на листе бумаги.
МОДЕЛЬ ОБЪЕМНОГО ТУМАНА
Использование слова "объемный" для описания тумана излишне: визуально туман выглядит так из-за взвешенных в воздухе частиц, занимающих в пространстве некоторый объем. Чем плотнее расположены частицы (т.е. чем больше частиц в единице объема), тем менее прозрачен туман. Взвешенные частицы блокируют свет, и цвет тумана есть не что иное, как цвет частиц. Если частицы красного цвета, вы видите красный туман. Чем меньше объем туманного участка, тем хуже сквозь него видно (тем больше частиц на пути). Значит, визуально эффект яркости тумана или количество частиц - это функция глубины объема тумана.
Есть три хорошо известных функции для моделирования тумана: линейный туман, экспоненциальный туман и экспоненциальный квадратный туман. По причинам, вскоре всплывущим на свет, я выберу только линейный туман.
Функция линейного тумана указывает, что яркость тумана прямо пропорциональна глубине объема тумана:
I = density * (Zmax - Zmin),
где I - яркость тумана в точке, density - плотность тумана, а Zmin, Zmax определяют глубину объема тумана.
Чтобы смешать цвет тумана в многоугольнике, уравнение во всех случаях одинаково:
C = I * F + (1 - I) * P,
где С - конечный цвет, I - яркость цвета, F - цвет тумана и Р - цвет многоугольника.
Это уравнение можно изменять (render), используя alpha-смешанные многоугольники с затенением Gouraud (alpha blended Gouraud shaded polygons). Просто замените I на alpha в предыдущем уравнении и используйте цвет тумана как цвет для затененного многоугольника. Когда я говорю многоугольник с затенением Gouraud, я имею в виду, что именно alpha интерполируется линейно, а не цвет, который остается постоянным.
ТУМАН В КЛЕТКАХ
В своей основе каждая клетка имеет плотность тумана и цвет тумана. Если тумана в клетке нет, то его плотность равна 0. Чтобы "рендерить" объем тумана, нужно вычислять яркость I по каждой вертикали, а для этого необходимо найти Zmin и Zmax.
Когда мы "рендерим" через портал, нам нужно вычислить плоскостное уравнение в видимом пространстве для этого портала, прежде чем войти в затуманенную клетку. Это плоскостное уравнение совпадает с началом объема тумана (оболочка клетки и есть объем тумана).
Данное плоскостное уравнение портала (в видимом пространстве):
Ax + By + Cz + D = 0
Отсюда мы легко получим
Zmin = Z = - (Ax + By + D) / C
Подобным образом, для каждого многоугольника за порталом мы берем его плоскостное уравнение в видимом пространстве, чтобы получить:
Zmax = Z = - (Ax + By + D) / C
Итак, для "рендеринга" каждого многоугольника через портал нам необходимо вычислить alpha = density * (Zmax - Zmin) для каждой вертикали. Затем генерируете многоугольник тумана с использованием значений alpha и "рендерите" сверху исходного многоугольника. Если устройства аппаратного обеспечения обеспечивают прямую поддержку линейного тумана, то нет даже необходимости "рендерить" многоугольники тумана во втором заходе. Если же hardware не поддерживает линейный туман, придется использовать alpha-смешение во втором заходе.
Причина, по которой я взял линейный туман, состоит в том, что можно использовать многоугольники с затенением Gouraud для "рендеринга" многоугольников тумана. Другая причина в том, что DirectX и, возможно, большая часть аппаратного обеспечения на данный момент поддерживают только линейный туман.
ПРОБЛЕМА
Возьмем одну клетку с туманом поверх другой клетки с туманом. В обеих клетках использован туман одного цвета и одинаковой плотности. Обе клетки находятся в плоскости, кторая включает портал между ними. Так как фактор ( Zmax - Zmin ), скорее всего, будет различен, яркость тумана также будет различаться. Используя линейный туман, я легко могу продемонстрировать, что линейность будет разорвана, если "рендерить" каждый объем тумана по отдельности:
T = Ia * F + ( 1 - Ia ) * P (1)C = Ib * F + ( 1 - Ib ) * T (2)
Замените (1) на (2) и вы получите:
C = Ib * F + ( 1 - Ib ) * ( Ia * F + ( 1 - Ia ) * P )
Поменяв местами, получаем:
C = (Ia + Ib - Ia * Ib ) * F + ( 1 - ( Ia + Ib - Ia * Ib ) ) * P
Если использовать I = ( Ia + Ib - Ia * Ib ), получим:
C = I * F + ( 1 - I ) * P
Теперь, если I - линейное, то все будет в порядке. Если же нет, решение будет не точным.
Если мы запишем Ia и Ib как:
Ia = density * ( Z3 - Z2 ) Ib = density * ( Z2 - Z1 )
Тогда можно записать I как:
I = density * ( Z3 - Z2 ) + density * ( Z2 - Z1 ) - density * ( Z3 - Z2 ) * density * ( Z2 - Z1 )
Это, ясное дело, не линейное уравнение. Точное решение для линейного тумана выглядит следующим образом:
I = density * ( Z3-Z1 )
РЕШЕНИЕ ПРОБЛЕМЫ
Первое решение простое: просто проигнорируйте это. Все может выглядеть неплохо, может не так уж хорошо, покажет только проверка. Большего следует ожидать от претворения в жизнь нескольких объемов тумана.
>Второе решение состоит в сгруппировке объемов тумана вместе. Оно будет действенным, только если цвет и плотность тумана одинаковы во всем соединенном объеме тумана. В этом случае можено использовать идею "комнат", состоящих из множественных клеток. Это решение не работает, если комбинируются различные цвета тумана.
>Теперь я считаю, что первого решения будет вполне достаточно. Оно не самое верное, но речь здесь идет о трехмерной экшн-игре (3D action game), и скорость в ней более важна. Соединение объемов тумана - не самое быстрое занятие.
Сравнение между решением первым и точным решением видно в типичной игровой сцене:
Density = 1/100
Z3 = 90
Z2 = 60
Z1 = 40
Решение 1: I = 0.44
Точное решение: I = 0.5
Вот здесь ясно видна погрешность в 12%. Я полагаю, для большего количества клеток она возрастет. Это означает, что лучше использовать большие клетки с туманом, чем множество мелких. И только проверка может показать, верно ли первое решение.