• Партнер

  • Чтобы не считать честно распределение плотности тумана для каждого пикселя, как обычно, вводят некоторую аппроксимацию, что в нашем случае приводит к понятию "вертексного тумана" (vertex fog). Т.е. мы рассчитываем распределение тумана для вершин полигонов находящихся в объеме тумана, а затем полученные значения интерполируем по поверхности полигона.

    Программеры Unreal решили, что линейной интерполяции им будет недостаточно и что обязательно нужно использовать честные формулы. В результате в Анрыле мы имеем так называемые "карты тумана" (fog maps) - дополнительные текстуры, которые аддитивно накладываются на полигоны и создают тем самым эффект тумана. Основная фишка фогмэпов состоит в том, что их нужно
    перерассчитывать каждый кадр, что влечет за собой громоздкие вычисления (несколько тысяч линейных интегралов по объему каждый кадр), поэтому чтобы хоть как-то сэкономить время, фогмэпы рассчитываются в небольших разрешениях и накладываются с помощью дополнительных конвейеров мультитекстурирования.

    Кармак поступил намного проще, для отрисовки тумана в третьем Квейке он воспользовался ни чем иным, как обыкновенной заливочкой Гуро, которая хоть и не является перспективно-корректной, но, тем не менее, позволяет добиться неплохих результатов малой кровью. Рассмотрим данный метод подробнее.

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

    Допустим наша сцена состоит из одного браша заполненного туманом и нескольких брашей геометрии:

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

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

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

    // структура описывающая плоскость
    typedef struct {
    float normal[3]; // нормаль к поверхности (A, B, C)
    float dist; // dist = A*x + B*y + C*z, где (x, y, z) - это
    // координаты любой точки принадлежащей плоскости
    }

    // вертекс (x, y, z)
    typedef float vec3_t[3];

    // p - плоскость пересечение с которой находим;
    // start, end - начальные и конечные координаты луча;
    // out - искомая точка пересечения плоскости и луча;
    //
    int GetIntersectionPoint (plane_t p, vec3_t start, vec3_t end, vec3_t out)
    {
    float dist1, dist2, dot;

    // найдем расстояние от start до плоскости
    dist1 = start[0]*p.normal[0] + start[1]*p.normal[1] + start[2]*p.normal[2];
    dist1 -= p.dist;

    // найдем расстояние от end до плоскости
    dist2 = end[0]*p.normal[0] + end [1]*p.normal[1] + end[2]*p.normal[2];
    dist2 -= p.dist;

    // если луч не пересекает плоскость, то выходим
    if (dist1*dist2 > 0) return 0;

    // найдем точку пересечения
    dot = dist1 / (dists1 - dist2);
    out[0] = start[0] + dot*(end[0] - start[0]);
    out[1] = start[1] + dot*(end[1] - start[1]);
    out[2] = start[2] + dot*(end[2] - start[2]);

    return 1;
    }

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

    // fogcolor - цвет тумана;
    // density - плотность тумана, т.е. минимальная толщина слоя
    // тумана, когда он становится непрозрачным;
    // start - точка вхождения луча в браш тумана;
    // end - вершина в тумане к которой проводили луч;
    // color - результирующий цвет;
    //
    void CalcVertexFogging (vec3_t fogcolor, float density, vec3_t start, vec3_t end, vec3_t color)
    {
    float dist, alpha;

    // найдем расстояни между точками - путь луча в тумане
    end[0] -= start[0];
    end[1] -= start[1];
    end[2] -= start[2];

    dist = sqrt (end[0]*end[0] + end[1]*end[1] + end[2]*end[2]);

    // рассчитаем прозрачность тумана
    alpha = min(1.0, dist/density);

    // и результирующий цвет
    color[0] = alpha * fogcolor[0];
    color[1] = alpha * fogcolor[1];
    color[2] = alpha * fogcolor[2];
    }

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

    Графические API и вертексный туман

    Вертексный туман официально поддерживается такими низкоуровневыми графическими API как DirectX и OpenGL. Для этого, к атрибутам вершин: координатам, цвету, координатам текстуры, координатам нормали и т.д. был добавлен еще один атрибут - координата тумана (fog coordinate), которая позволяет непосредственно задать значение плотности тумана для каждой вершины. И затем, при построении изображения, API использует эту координату и текущие установки тумана (цвет, плотность) для отрисовки полигонов с необходимыми цветовыми изменениями, вносимыми туманом.

    В OpenGL использование координат тумана возможно только для драйверов поддерживающих расширение GL_EXT_fog_coord. Перспективная коррекция для тумана, равно как и для текстур контролируется параметром GL_PERSPECTIVE_CORRECTION_HINT. Прототип функции для задания значения координаты тумана имеет следующий вид: void glFogCoord[fd]EXT (T coord);

    Для DirectX инициализация вертексного тумана осуществляется практически так же, как и пиксельного, нужно только для метода SetRenderState, вместо константы D3DRS_FOGTABLEMODE передать D3DRS_FOGVERTEXMODE. А чтобы самому задать значение плотности тумана для вертекса, нужно вычислить прозрачность тумана, как это показано выше, и поместить полученное значение в альфа-компоненту отраженного (specular) цвета вершины.

    Вычисления и рендеринг вертексного тумана для карточек, поддерживающих модуль T&L, выполняется аппаратно, что нельзя не поприветствовать бурными аплодисментами. Ура, товарищи!

    Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии