Latex formulas

воскресенье, 5 февраля 2012 г.

Простой ландшафт


Помнится, была такая программка "ландшафты Марса". Попробуем сделать нечто подобное. В этой программе будет использоваться карта высот 64x64 точки. По ней мы будем рендерить на экран картинку, причем камера сможет перемещаться с 4 степенями свободы: 3 поступательным и 1 вращательной. Обычно, в играх используются 5 степеней: те же поступательные (кнопки w-s, a-d + прыжки) и 2 вращательных (посмотреть вверх-вниз, влево-вправо). От обзора вверх-вниз пока откажемся.

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

На рисунке а) видна плоскость экрана и проекция его на карту высот. На б) видна карта высот и система координат, связанная с ней. К тому же с проекцией плоскости экрана тоже свяжем СК y', x' (на рисунке, увы, её нет, но догадаться несложно). Ориентацию экрана в пространстве зададим координатами posx, posy, posz (свяжем с ними любую фиксированную точку экрана) и углом φ между нормалью к экрану и осью x.

Переход от координат, связанных с экраном к тем, что связанны с картой высот таков:


Как мы будем строить картинку? Разобьем наш экран на столбцы и пронумеруем их от 0 до 799 (я строил картинку для разрешения 800x600). Мы будем пускать лучи (на самом деле отрезки;) из точки с координатами posx, posy, posz (координаты глаза) под разными углами β к оси x' и углом α к карте высот. Угол β возьмем пропорциональным номеру столбца.


Зададим ещё одну переменную - d, показывающую проекцию луча на ось x'
Проекция наших лучей на карту высот должна быть такая, как показанно на рисунке (я очень долго экспериментировал, прежде чем получить нужное):

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

Координаты x' и y' будут изменятся по ходу луча так:

К тому же координата z (высота от карты высот до точки на луче) изменится так:


Соответственно, для любой точке на лучах, заданной параметрами d и sx можно получить x и y - координаты проекции этой точки на карту высот, сначала расчитав x' и y', а потом и нужные координаты по формулам поворота.

Алгоритм заключается в том, чтобы запустить цикл по координате sx (номеру столбца) и d (длине проекции луча) и по высоте нашей поверхности в точках (x,y), отвечающих параметрам (sx,d) восстановить номер строки на экране.

Пусть h - высота поверхности в точке (x, y), а z - высота точки с проекцией (x,y) на некотором фиксированном луче.

Номер строки восстановим так:
sh/2 = 600/2 = 300 - выбранный нами уровень горизонта, k - некий коэффициент пропорциональности (подобрать по вкусу). Что это значит? Чем объект выше, тем отклонение от горизонта больше, а чем он дальше - тем он ближе к горизонту.

Теперь нужно только отрисовать столбик на экране с координатами (sx,syi), где syi изменяется от 0 (низ экрана) до sy.

Но что, если при увеличении d таким образом мы затрем объект, который расположен ближе к нам? Есть два выхода:

1) простой и медленный. Рисовать всё, но начинать с более удаленных объектов (уменьшая d)
2) чуть сложный и куда более быстрый. Рисовать, начиная с близких объектов (увеличивая d), но создать массив, где будут храниться максимальные уже нарисованные sy для данного столбика. Тогда нам нужно будет выполнить проверку

if (sy>maxh[sx])
{
draw();
maxh[sx] = sy;
}
draw() рисует столбец от позиции (sx, maxh[sx]) до (sx, sy). Цвет для рисования можно взять из ещё одного массива 64x64. Ещё одна маленькая хитрость: что, если координата x и y выйдет за пределы 64x64? Ничего страшного, в карте высот, представленной в виде одномерного массива, индекс элемента с координатами (x,y) можно получить так:

int i,x,y;
....
i = ((64*x)&0xfc0 + (y&0x3f));


0xfc0 = 64*64 - 64
0x3f = 64 -1

В двоичной системе эти числа представляются как 111111000000 и 111111, что сразу делает вещи ясными: с помощью этих операций побитового И мы периодически продолжаем карту 64x64 докуда только хватит типа int.


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

Приведу готовую программу. Для компиляции нужна готовая библиотека SDL. Собирать так:

cc -I/path/to/SDL/headers -L/path/to/SDL/library -lm -lSDL -o test test.c


На FPS я её не ограничивал, так что не удивляйтесь, что будет жрать весь процессор.

Код готовой программы

Комментариев нет: