Войти
ПрограммированиеСтатьиГрафика

Вершинное освещение совместно с LightMaps.

Сейчас уже никого не удивишь наличием LightMap'ов в программе. И уж тем более наличием вершинного освещения. Но при всем при этом не надо сразу выбрасывать данную тему на помойку.

Автор: terror

Раньше вершинное освещение не могло давать качественных результатов из-за того, что карты не могли рисовать достаточное количество треугольников на кадр, при которых вершинное освещение имело бы силу. Поэтому и получалось что-то типа того:

Изображение

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

Теперь попробуем сравнить оба метода.

LightMaps.



VertexLight.

Вывод: LightMap — качество, VertexLight – количество.

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

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

Вот картинка из примера, прилагаемого к этой статье:

Изображение

На картинке нарисован чайник, который отбрасывает тень на сцену. При этом свет не проходит через него. Заметьте, что тень нарисована довольно качественно и самое главное - это не LightMap'ы. Это и есть то самое вершинное освещение.

Количество полигонов на чайнике довольно высокое и только поэтому тень на нем смотрится неплохо.

Вывод: при использовании высокополигональных объектов лучше использовать вершинное освещение. При этом LightMap'ы использовать как способ создания качественных теней.

Теперь, что касается примера. Это стандартное glut приложение, сделанное на VC++7.0, никаких особенностей при переводе в другие редакторы нет, кроме #pragma once.

Теперь более подробно разберем самую главную функцию в этом примере:

void InitVertexLight ( void )
{
  int i = 0;
  vector c;
  float  a,d;
  for ( int j = 0; j < Obj[1].VertsCount; j++ )
  {
    c.Set ( 130.0f );
    if ( lpos.GetDistance ( Obj[1].verts[j] ) > lrad ) continue;
    if ( RayTrace ( lpos, Obj[1].verts[j] ))
    {
      Obj[1].tVertexLight[j].v[0] = (byte)c.v[0];
      Obj[1].tVertexLight[j].v[1] = (byte)c.v[1];
      Obj[1].tVertexLight[j].v[2] = (byte)c.v[2];
      continue;
    }
    d = lpos.GetDistance ( Obj[1].verts[j] );
    a = 1.0f - ( d / lrad );
    c.v[0] += 255.0f * a;
    c.v[1] += 255.0f * a;
    c.v[2] += 255.0f * a;
    if ( c.v[0] > 255 ) c.v[0] = 255; if ( c.v[0] < 0 ) c.v[0] = 0;
    if ( c.v[1] > 255 ) c.v[1] = 255; if ( c.v[1] < 0 ) c.v[1] = 0;
    if ( c.v[2] > 255 ) c.v[2] = 255; if ( c.v[2] < 0 ) c.v[2] = 0;
    Obj[1].tVertexLight[j].v[0] = (byte)c.v[0];
    Obj[1].tVertexLight[j].v[1] = (byte)c.v[1];
    Obj[1].tVertexLight[j].v[2] = (byte)c.v[2];
  }
}

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

Поэтому в функции InitVertexLight() мы сразу пробегаемся по каждой вершине второго объекта. А дальше алгоритм таков:

1. Ставим цвет тени в 130. Вообще это можно делать и более продвинутыми методами, но и этот сгодится.

2. Так как источник света всего один, то проверяем сразу его. Если расстояние от лампочки больше радиуса освещения, то пропускаем вершину. Хотя и в данном примере это не видно из-за маленькой сцены, но все-таки лучше не пропускать вершину, а назначить ей цвет. Черный или серый.

3. Находим факт о пересечении луча от источника света до вершины.

4. Далее опять находим расстояние от лампочки до вершины. Для оптимизации можно делать всего один раз.

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

- Находим расстояние от источника света до вершины

- Получаем значение attenuation (a) по формуле: 1 - ( d / rad )
Где,
d - расстояние от источника света до вершины,
rad - радиус источника света (радиус освещения)

- Затем находим RGB вершины по формуле: Color * a * b (находится для каждой RGB составляющей)
Где,
Color — цвет источника света (R, G или B)
a — attenuation
b — brightness
Но в нашем примере параметр brightness мы опустили.

5. Проверяем чтобы числа не вылезли за пределы и присваиваем их цвету вершине.

Фишка в том, что цветовые параметры LightMap'а должны совпадать с вершинным цветом, так как в противном случае можно увидеть разницу между двумя методами, разницу в цвете. А это может плохо сказаться на картинке в сторону "глюкавости". Хотя иногда можно и не следить за схожестью цветовых параметров другого освещения. Например, у нас есть стена, а в ней проходят трубы, их, разумеется, видно. Так вот, можно придать трубам дополнительный вид если использовать на них вершинное освещение, а так как трубы находятся в стене, их подсветка не тронет общий цвет стены. Я все это к тому, что вершинное освещение и LightMap'ы не совсем схожи в конечном результате, поэтому нужно следить, чтобы разные объекты находящиеся под одной лампой были освещены одинаково, как LightMap'ами, так и вершинным освещением.

Исходный код к статье: 20030611.zip

#lightmaps, #освещение

11 июня 2003 (Обновление: 17 сен. 2009)

Комментарии [6]