Студопедия

Главная страница Случайная страница

КАТЕГОРИИ:

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Вычисление масштаба






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

Для вычисления масштаба мы применяем сравнение между Х- и Y-пересечениями. Масштаб или высота вычисляется на основе ближайшего пересечения. Когда мы вычисляем масштаб битовой карты, мы можем использовать само расстояние:

масштаб = расстояние до пересечения

Это не всегда срабатывает: приближаясь, объекты становятся больше, но не уменьшаются при удалении. Тогда можно применить такую формулу:

масштаб = 1 / расстояние до пересечения

Это работает. Но результат надо умножить на некоторый коэффициент расстояния (или правдоподобия — как вам больше нравится).

масштаб = К / расстояние до_пересечения

где К стоит подобрать самостоятельно.

Уменьшение проекционных искажений

Проблема, о которой нам надо поговорить, заключается в проекционном искажении. Как вы знаете, для реализации отсечения лучей мы нарушили правило и использовали одновременно полярные и декартовы системы координат. Это привело к эффекту «рыбьего глаза» (то же самое возникает, когда вы смотрите сквозь сильную линзу).

Это сферическое искажение возникает вследствие использования нами при трассировке лучей радиального метода. Мы рассчитываем все лучи, выходящие из одной точки (позиции игрока). Сферические искажения возникают потому, что все объекты, с которыми пересекаются лучи, определены в «прямоугольном» пространстве. Расчет же лучей проводится в «сферическом» или «полярном» пространстве. Пример такого пространства представлен на рисунке 6.27.

Теперь посмотрим на рисунок 6.28. Мы увидим, что наблюдает игрок, когда смотрит прямо на стену. Он видит прямоугольник. Но так как расстояния до точек пересечения различны, изображение получается искаженным. Рисунок 6.29 показывает два результата отсечения лучей. Первый построен с учетом компенсационных искажений, а второй — без их учета. Все это очень интересно, но как это реализовать? Ответ прост: нужно умножить функцию масштаба на инверсную функцию. Синусоидальное искажение может быть компенсировано умножением масштаба на cos-1 текущего угла по отношению к полю наблюдателя (60 градусов). То есть мы должны умножить каждое значение угла от -30 до +30 на cos-1 того же угла. Это исключит искажение.

 

Отрисовка фрагментов стен

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

top = 100 - scale/2 // верхняя граница

bottom = top + scale // нижняя граница

где scale — окончательные вертикальные размеры фрагмента.

Реализация отсектеля лучей

Я написал совершенно полную реализацию алгоритма отсечения лучей. Он закомментирован и даже содержит какую-то логику. Эта программа включена в комплекте поставки на дискете в файле RAY. С. Демонстрационная программа загружает двухмерную карту мира в виде ASCII-файла. На рисунке 6.30 приведена подобная карта. Она создается с помощью обычного текстового редактора.

Вся программа слишком длинна, чтобы включить ее в книгу, поэтому здесь приведена только ее самая интересная часть: процедура отсечения лучей. Попробуйте с ней разобраться и понять, что это и зачем так сделано.

 

 
 
1 1 1 11111 1 1 1 1 1 1 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 1 11 111111 1 1 1 1 1 1 111 1 1 1 1 1 1 1 11111111 1 1 1 1

 


Листинг 6.4. Процедура отсечения лучей (RAYLIST.C)

void Ray_Caster(long x, long y, long view_angle)

{

// эта функция выполняет расчет 320 лучей и строит игровой

// экран на основе их пересечений со стенами. Расчет производится

// таким образом, что все лучи отображаются на поле просмотра

// с углом 60 градусов

// после расчета траекторий лучей, рассчитываются координаты

// их пересечений со стенами. Координаты первой точки пересечения

// запоминаются. Ближайшие к игроку точки используются для

// построения битового образа изображения. Расстояния используются

// для определения высоты текстурных фрагментов и вертикальных линий

// Примечание: эта процедура использует функции стандартной

// библиотеки компилятора для работы с плавающей точкой

// (это работает медленно). Код не оптимизирован (еще медленнее),

// и в довершение всего обращается к функциям графической

// библиотеки компилятора фирмы Microsoft (что уж вовсе

// никуда не годится!) // Однако все это имеет определенную цель - легкость

// для понимания того, как работает процедура

int rcolor;

long xray=0, // счетчик вертикальных пересечений

yray=0, // счетчик горизонтальных пересечений

next у cell, // используются для вычисления

next_x cell, // номера следующей ячейки по ходу луча

cell_x, // координаты текущей ячейки

се11_у, // луча

x_bound, // следующие вертикальная

у_bound, // и горизонтальная точки пересечения

xb_save, // в этих переменных запоминаются

yb_save, // координаты точек пересечения

x_delta, // эти переменные показывают, на сколько

y_delta, // надо сместиться для перехода к следующей ячейке

ray, // текущий луч для отсечения

casting=2, // показывает компоненты Х и Y луча

x_hit_type, // координаты блока, с которым пересекся луч

y_hit_type, // используются при отрисовке

top, // верхняя и нижняя координаты области,

bottom; // отрисовываемой как стена (с помощью текстуры)


float xi, // используется для определения х- и у-пересечений

yi,

xi_save, // используется для сохранения

// точек пересечения Х и У

yi_save,

dist_x, // расстояние до х- и у-пересечениЙ

dist_у, // от точки просмотра scale;

// масштаб

//СЕКЦИЯ 1 ////////////////////////////////////////

// инициализация

// вычисляет начальный угол от игрока. Поле просмотра 60 градусов.

// Таким образом, рассматриваем только половину - 30 градусов

if ((view_angle-=ANGLE_360) < 0)

 

// разворачиваем вектор направления взгляда

view_angle=ANGLE_360 + view_angle;

} // конец оператора if

// выбираем цвет для луча

rсо1оr=1 + rand()%14;

 

//СЕКЦИЯ 2 ////////////////////////////////////////

// цикл для всех 320 лучей

for (ray=0; ray< 320; ray++)

// вычислить первое х-пересечение

if (view_angle > = ANGLE_0 & & view_angle < ANGLE_180)

{

// вычислить первую линию, которая пересекается с лучом.

// Примечание: эта линия должна быть выше (впереди

// на игровом поле) игрока.

y_bound = CELL_Y_SIZE + CELL_Y_Sf2E * (у / CELL_Y_SI2E);

// вычислить смещение для перехода к следующей

// горизонтальной линии

y_delta = CELL_Y_SIZE; // размер ячейки по вертикали (ред.)

// основываясь на первой возможной горизонтальной линии отсечения,

// вычислить Х-пересечение и начать расчет

xi = inv_tan_table[view_angle] * (y_bound - у) + х;

// установить смещение

next_у_cell = 0;

} // конец обработки верхней половины плана

else

{ // вычислить первую горизонтальную линию, которая может

// пересекаться с лучом. Это будет позади игрока

y_bound = CELL_Y_SI2E * (у / CELL_Y_SIZE);

// вычислить смещение для следующей горизонтальной линии

y_delta = -CELL_Y_SIZE;

// основываясь на первой возможной горизонтальной линии отсечения,

// вычислить Х-пересечение и начать расчет

xi = inv_tan_table[view_angle] * (y_bound - у) + х;

next_y_cell = -1;

} // конец обработки нижней половины плана

//СЕКЦИЯ 3 ////////////////////////////////////////

// вычислить первое х-пересечение

if (view_angle < ANGLE_90 || view_angle > = ANGLE_270) {

// вычислить первую вертикальную линию, которая будет

// пересекаться с лучом. Она должна быть справа от игрока

x_bound = CELL_X_SIZE + CELL_X_SIZE * (х / CELL_X__SIZE);

// вычислить смещение

x_delta = CELL_X_SIZE;

// основываясь на первой возможной вертикальной линии отсечения,

// вычислить Y-пересечение и начать расчет

yi = tan_table[view_angle] * (x_bound - х) + у;

next_x_cell = 0;

} // конец обработки правой половины плана

else

{

// вычисляем первую вертикальную линию, которая может быть

// пересечена лучом. Она должна быть слева от игрока

x_bound = CELL_X_SIZE * (х / CELL_X_SIZE);

// вычислить расстояние до следующей вертикальной линии

x_delta = -CELL_X_SIZE;

// основываясь на первой возможной вертикальной линии отсечения,

// вычислить Y-пересечение

yi = tan_table[view_angle] * (x__bound - x) + у;

next_x_cell = -1;

}

// начать отсечение

casting =2; // два луча для одновременного отсечения

хrау = уrау = 0; // сбросить флаги пересечения

//СЕКЦИЯ 4 ////////////////////////////////////////

while(casting)

{

// продолжить отсечение лучей

if (xray! =INTERSECTION_FOUND)

{

// тест на совпадение луча с асимптотой

if (fabs (y_step[view_angle])==0)

xrау = INTERSECTION_FOUND;

casting--;

dist_x = 1e+8;

} // конец проверки на совпадение с асимптотой

// вычислить текущую позицию карты для проверки

сеll_х = ((x_bound+next_x_cell) / CELL_X_SIZE);

cell_y = (long)(yi / CELL_Y_SIZE);

// проверить, есть ли в этом месте блок

if ((x_hit_type = world[(WORLD_ROWS-1) - cell_y][cell_x])! =0)

{

// вычислить расстояние

dist_x = (yi - y) * inv_sin__table[view angle];

yi_save == yi;

xb_save = x_bound;

// закончить х-отсечение

хrау = INTERSECTION_FOUND;

casting--;

} // конец проверки попадания луча на стену блока

else

{

// вычислить следующее Y-пересечение

yi += y_step[view_angle];

} //конец оператора else

} // конец проверки на отсечение по оси Х

//СЕКЦИЯ 5 ////////////////////////////////////////

if (yray! =INTERSECTION_FOUND)

{

// тест на попадание луча на асимптоту

if (fabs(x_step[view_angle])==0)

{

уrау = INTERSECTION_FOUND;

casting--;

dist_y=1e+8;

}

 

// вычислить позицию карты

ceil_x = (long)(xi / CELL_X_SI2E);

cell_y = ((y_bound + next_y_cell) / CELL_Y_SIZE);

// проверить, находится ли в этом месте блок

if ((y_hit_type = world[(WORLD_ROWS-1) - cell_y] [cell_x])! =0)

{

// вычислить расстояние

dist_y = (xi - х) * inv_cos_table[view angle];

xi_save = xi;

yb_save = y_bound;

// закончить вычисление Y-пересечения

yray = INTERSECTION_FOUND;

casting--;

} // конец обработки попадания луча на блок

else

{

// вычислить следующее Х-пересечение

xi += x_step, [view_angle];

} // конец оператора

else

} // конец проверки на отсечение по оси У

// перейти к следующей точке пересечения

x_bound += x__delta;

y_bound += y_delta;

}

//СЕКЦИЯ 6 ////////////////////////////////////////

// выяснить, какая из стен ближе - вертикальная или горизонтальная

// и затем нарисовать ее

// Примечание: в дальнейшем мы заменим вертикальную линию на

// текстурный блок, пока же достаточно того, что есть

if (dist_x < dist_y)

{

sline(x, y, (long)xb_save, (long)yi_save, rcolor);

// вертикальная стена ближе горизонтальной

// вычислить масштаб и умножить на поправочный коэффициент

// для устранения сферических искажений

scale = cos_table[ray]*15000/(1e-10 + dist_x);

// вычислить координаты верха и низа

if ((top = 100 - scale/2) < 1) top =1;

if ((bottom = top+scale) > 200) bottom=200;

// нарисовать фрагмент стены

if (((long)yi_save) % CELL_Y_SIZE < = 1) _setcolor(15);

else

_setcolor(10);

_moveto((int)(638-ray), (int)top);

_lineto((int)(638-ray), (int)bottom);

}

else // сначала надо нарисовать горизонтальную стену

{

sline(x, y, (long)xi_save, (long)yb_save, rcolor);

// вычислить масштаб

scale = cos_table[ray]*15000/(le-10 + dist_y);

// вычислить координаты верха и низа

if ((top = 100 - scale/2) < 1)

top = 1;

if ((bottom = top+scale) > 200) bottom=200;

// нарисовать фрагмент стены

if (((long)xi_save) % CELL_X_SIZE < = 1)

_setcolor(15);

else

_setcolor(2);

_moveto((int)(638-ray), (int)top);

_lineto((int)(638-ray), (int)bottom);

} //конец оператора else

//СЕКЦИЯ 7 //////////////////////////////////////

//отсечь следующий луч

if (++view_angle> =ANGLE_360)

{

// установить угол в 0

view_angle=0;

} // конец оператора if

} // конец цикла for по лучам

} // конец функции

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

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

§ Вторая и третья части вычисляют Х- и Y-пересечения текущего луча с периметром ячейки, в которой находится игрок. После того как найдено первое пересечение как с осью X, так и с осью Y, устанавливается значение нескольких переменных для определения траектории луча по отношению к осям координат. Информация, полученная в этих частях, используется в четвертой и пятой части;

§ Четвертая и пятая части продолжают проверку пересечений. Каждое из пересечений с координатной осью проверяется на пересечение с объектом. Если это происходит, то вычисляется дистанция и запоминается для дальнейшего использования. Эта информация может быть использована для тексту рирования объектов. Хотя рассмотренный нами трассировщик лучей и не делает этого, он предоставляет достаточно информации для текстурирования. Например, если луч пересекает середину стены блока, это означает, что мы должны вывести на экран 32-ю вертикальную полосу соответствующей текстуры. Более подробно этот вопрос будет рассмотрен позднее, в главе, посвященной описанию игры Warlock;

§ В шестой части заканчивается обработка луча. К этому моменту мы уже вычислили горизонтальные и вертикальные пересечения и запомнили расстояния до них. Следовательно, мы готовы нарисовать на экране соответствующую лучу вертикальную полосу. Для этого мы определяем, которое из пересечений находится ближе всего. Горизонтальная позиция для отрисовки соответствует номеру текущего луча и меняется в диапазоне от 0 до 319. Высота рисуемого фрагмента вычисляется на основании расстояния до игрока и с некоторыми корректировками для улучшения впечатления;

§ Седьмая часть увеличивает текущий угол и осуществляет переход к первой части. Цикл выполняется до тех пор, пока все 320 лучей не будут отсечены.

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

Оптимизация отсекателя лучей

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

§ Первое и обязательное условие — это использование целочисленной арифметики вместо операций с плавающей запятой. Это позволит увеличить скорость примерно в 2-4 раза;

§ Во-вторых, мы можем многое оптимизировать в самом тексте программы на Си. Это поднимет быстродействие примерно на 20-50 процентов;

§ Далее, мы можем разбить Х- и Y-отсечения на две независимые программы. Это позволит производить вычисления параллельно;

§ И, наконец, никакая нормальная программа не станет использовать графическую библиотеку Microsoft или разрешение 640х480.

Если использовать уже написанные функции для режима 13Ь, то выполнение программы ускорится в 10 раз и она начнет работать со скоростью 30 кадров в секунду. Но даже если скорость окажется около 15 кадров в секунду, то я буду рад.

Отрисовка дверей и прозрачных областей

Я надеюсь, вы видели, как открываются двери в Wolfenstein'e и DOOM'e. Когда они открыты, вы можете заглянуть в соседнюю комнату. С точки зрения трассировки лучей, при этом возникает множество проблем. С одной стороны, Дверь должна быть нарисована. С другой — должно быть нарисовано и то, что находится позади нее. Для решения этой проблемы луч трассируется до пересечения с дверью и затем проходит сквозь нее.

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

Это достигается следующим образом:

1. В процессе трассировки луч достигает сплошной стены.

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

3. Запоминается полученное пересечение.

Этот процесс может продолжаться до бесконечности, но сейчас давайте предположим, что он прекращается после пересечения второй стены. Как показано на рисунке 6.31, мы получаем два пересечения.

Первое пересечение позволит отрисовать соответствующую вертикальную полосу. Однако второе пересечение тоже даст вертикальную полосу. Корректная отрисовка обоих вертикальных фрагментов осуществляется в соответствии с Алгоритмом Художника. Таким образом, вначале на основе полученных от второго пересечения данных будет отрисован более удаленный фрагмент стены, который затем будет частично перекрыт ближним фрагментом. Полученное таким образом изображение и будет выглядеть так, как если бы мы заглядывали в соседнюю комнату через окно.

Аналогично обрабатывается и открывание двери. Если дверь открывается вправо или влево, она частично уходит внутрь косяка. Таким образом, часть изображения физически исчезает с переднего плана. Для создания такого эффекта мы должны продолжить трассировку луча после пересечения с дверью. Однако в отличие от окон, убранная часть двери является полностью прозрачной, поэтому нам не надо запоминать оба пересечения. Мы вполне можем ограничиться отрисовкой изображения, полученного на основе пересечения луча с удаленной стеной. В общем, все эффекты типа окон, открывающихся дверей.и т. п. создаются тем же способом, что и рассмотренные примеры — сочетанием трассировки луча через прозрачные области и простого механизма Z-буферизации.

Освещение, тени и палитра

Я хочу вам рассказать, каким образом выполнены световые эффекты в DOOM и как это можно реализовать. Более подробно об этом можно будет прочитать в седьмой главе.

Дело в том, что в играх, подобных DOOM, используется куча разных уловок и трюков. Более того, редактор карт, с которым мы познакомимся позже, позволит работать с планами комнат, цветом и т. д. В общем, все, что связано с цветовыми эффектами в DOOM, сделано с помощью цветовой палитры.

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

Рассеянное освещение

Рассеянное освещение создает находящийся в комнате источник света. Например, если у вас есть регулируемая лампа, то вы можете изменять уровень рассеянного освещения. Рисунок 6.32 показывает комнату при двух разных уровнях рассеянного освещения.

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

Локальное освещение

Локальное освещение представляет собой концентрацию интенсивности света в определенном направлении с целью освещения ограниченной части помещения. Например, если, лежа в кровати, вы включите фонарик и направите его на стену, то увидите световое пятно (рис. 6.33). Если же вы включите свет, то, даже несмотря на рассеянное освещение, увидите, что область, освещаемая фонариком, сохранится до тех пор, пока интенсивность локального и рассеянного освещения не станут равны.

 

Закон Ламберта

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

Id = Ii Kd cosQ 0< = Q < = Pi/2

где:

• Id - результирующая интенсивность;

• Ii - интенсивность источника;

• Kd — коэффициент отражения материала;

• Q - угол между нормалью и направлением освещения.

Выражаясь нормальным языком, это означает, что отражение света возрастает, когда направление на источник света становится коллинеарным с нормалью к отражающей поверхности, или, иными словами, когда отражающая поверхность размещена прямо за источником света.

Закон обратных квадратов

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

Создание модели освещения

Сейчас мы уже изучили всю физику, которую надо знать для формирования хорошо выглядящей модели освещения. Мы знаем, что:

§ Чем дальше находится источник, тем меньше света он дает;

§ Если поверхность расположена под углом к источнику света, она отражает меньше света;

§ Если изменяется уровень рассеянного освещения, это сказывается на всех объектах в комнате. Все эти три фактора, взятые в совокупности, и формируют модель освещения.

Нам известны: угод, под которым виден источник света, уровень рассеянного освещения и расстояние до каждой из стен. Возникает вопрос: все ли это нам нужно? Может быть все, а может быть и нет. Это зависит от того, что называть реалистичным. Наша главная задача — сформировать затенение стен так, чтобы они выглядели реальными, но, в то же время, изображение стены формируется с помощью текстуры.

Существует два пути устранения этого противоречия.

Мы могли бы рассчитывать интенсивность каждого пикселя " на лету" и окрашивать его в соответствующий цвет. Единственная, возникающая при этом проблема, — недостаточное количество одновременно отображаемых цветов. Мы располагаем только 256 регистрами цвета, следовательно, одновременно на экране не может присутствовать больше 256 цветов. Это заставляет нас использовать цвет с большой осторожностью.

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

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

На практике нам нет необходимости использовать оба параметра в алгоритме затенения. Мы можем использовать или расстояние от стены до игрока или угол между направлением взгляда и поверхностью стены для получения вполне реалистичного изображения. Единственная проблема состоит в том, как получить все возможные эффекты затенения с помощью только 256 цветов?

Для решения этой проблемы мы можем создать палитру со следующими свойствами:

§ Первые 16 цветов - стандартные цвета EGA;

§ Дополнительные цвета помещаются в следующие 56 ячеек палитры. Это те цвета, с которыми мы будем работать, и единственные цвета, присутствующие в игре. Эти 56 цветов должны быть выбраны так, чтобы их хватило в качестве базовых для изображения всех игровых объектов. Более того, эти цвета будут самыми яркими в игре и они должны создаваться с учетом этого факта;

§ Теперь некоторая хитрость. Оставшиеся 184 цвета разбиваются на 3 банка по 56 цветов в каждом и банк из 16 цветов в конце палитры.

Три дополнительных банка по 56 цветов будут заполняться в процессе работы программы и использоваться механизмом формирования затенения. Дополнительный банк из 16 цветов используется для анимации палитры. Таким образом, палитра будет выглядеть как Представлено в таблице.

Таблица 6.1. Цветовая палитра для формирования затенения.

Регистры цвета Функция
0 – 15 Базовые цвета EGA
16 - 71 Первичные цвета
72 – 127 Вторичные затененные производные первичных цветов
128-239 Третичные затененные производные
240 – 255 Дополнительные цвета для цветовой ротации и т.п.

Механизм затенения работает следующим образом: при отрисовке каждого вертикального фрагмента к значениям его пикселей добавляется некая константа. Например, если отрисовывается текстура из одного цвета с номером 16, то производные цвета будут иметь значения 16+56, 16+2х56 и 16+3х56 (или 72, 128 и 184 соответственно). В общем, механизм затенения берет за основу цвета текстуры и модифицирует их за счет использования других регистров цвета, номера которых определяются как сумма исходного цвета и константы. Если объект нарисован в первых 16 цветах, добавление константы не производится. Теперь мы имеем по четыре варианта каждого из 56 первичных цветов. Этого вполне достаточно для формирования реалистичного изображения. Три «затененных», банка цветов заполняются во время работы программы путем некоторого уменьшения интенсивности базовых цветов. Таким образом, всего получаются четыре банка из одних и тех же цветов с убывающей интенсивностью.

Теперь возникает вопрос, как пользоваться полученной таблицей и как готовить битовые карты? Когда вы будете рисовать свои битовые карты, рисуйте те объекты, которые впоследствии планируете затенять, с использованием 56 первичных цветов. Те объекты, которые затеняться не будут, рисуются в первых 16 цветах — они не затрагиваются механизмом затенения.

Кстати, так же создается и эффект локального освещения. Освещенные или светящиеся области рисуются с использованием первых 16 цветов и, соответственно, их цвет не изменяется при затенении.

Последняя проблема состоит в том, насколько мы должны затенять цвет. Как я уже говорил раньше, мы можем использовать или угол между поверхностью и лучом зрения или расстояние до игрока. Я рекомендую попробовать оба варианта и выбрать тот, который вам больше понравится. Что бы вы ни использовали, эта величина должна быть разбита на зоны. Если величина попадает в первую зону, используйте первую производную цвета, во второй зоне — вторую и т. д. Например, вы можете решить, что если угол составляет меньше 20°, то используется первая производная, если он оказывается в диапазоне 20-300 — вторая и т. д.

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

 

 

Таблица 6.2.

Дистанция (условных ед.) Банк цвета
0-20 Банк 0(цвета 16 – 71)
20-50 Банк 1(цвета 72 – 127)
50-100 Банк 2(цвета 128 – 183)
100-бесконечность Банк 3(цвета 184 – 239)

 

 

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

Коротко повторим: цветовая палитра разбивается на шесть областей.

§ Первая и последняя области содержат цвета, не используемые при затенении;

§ Четыре средних области представляют собой банки по 56 цветов и каждый из этих банков представляет собой производные одних и тех же первичных цветов.

Когда изображение выводится на экран, каждый пиксель рисуется наиболее ярким цветом (банк 0), к которому добавляется некоторая константа, для формирования требуемой производной. Эта константа зависит от модели освещения, определяемой либо расстоянием от объекта до игрока, либо углом, под которым игрок смотрит на поверхность.

Вот и все.

 

ИТОГ

В этой главе мы изучили основы трехмерной графики, поговорили о математических основах трехмерных трансформаций и методах, используемых для обсчета трехмерных образов (удаление невидимых поверхностей и т. д.).

Кроме этого, мы узнали о двух наиболее важных с точки зрения создания красивых DOOM-образных игр методах: о трассировке и отсечении лучей. Мы даже написали реализацию алгоритма отсечения лучей.

Позже, когда настанет пора написать игру Warlock, мы еще раз вспомним эти методы и реализуем все, о чем мы говорили в этой главе, в полном объеме. А пока давайте перейдем к следующей главе. Кстати, пока не забыл, если вы что-то не поняли, прочитайте и осмыслите главу еще раз.



Поделиться с друзьями:

mylektsii.su - Мои Лекции - 2015-2024 год. (0.04 сек.)Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав Пожаловаться на материал