Технология RayCasting ( Основы ) |
Несколько лет назад, поиграв в Wolfenstein-3D, я удивился почему можно поворачиваться только по одной оси. Так же этот вопрос не покидал меня, когда я играл в Doom, Blood, Duke Nukem 3D и подобные игры. Тогда я не знал что движки этих игр основаны на технологии, именуемой RayCasting. Когда я узнал о том, что же это за технология, все встало на свои места. Суть данной технологии в том, что для каждого пиксела изображения выпускается луч, который проверяется на пересечение с различными объектами сцены. Эта технология нераспространенна по причине своей ресурсоемкости. Современные компьютеры не способны справиться с такими просчетами в реальном времени. Но почему тогда вышеперечисленные игры так быстро работают? Неужели это чудо? На самом деле все значительно проще. Секрет такой скорости как раз в том, что игры эти скорее двухмерные чем трехмерные. Все просчеты происходят в двухмерном пространстве, а затем на основе полученных данных строится трехмерная картинка. Как уже было сказано выше, из глаз наблюдателя выпускается определенное количество лучей. В Wolfenstein-3D лучи выпускались только в одной плоскости, а трехмерное изображение строилось с использованием расстояния от точки наблюдателя, до ближайшей точки пересечения с обьектом. В моем прилагающейся программе, демонстрирующей элементарный RayCasting в деле, этих лучей 320. Разберем данный рисунок: Отрезок LM это гипотенуза равнобедренного треугольника видимости. Отрезок a это половина гипотенузы. Тоесть половина видимости, до вектора направления взгляда и высоты треугольника b. Соответственно c это положение наблюдателя. Лучи, выпускаемые из глаз наблюдателя, можно представить как массив векторов единичной длины. Функция, которая создает эти вектора в прилагающемся примере называется rcInitDir. Ей не передаются какие-либо параметры.
Ширина экрана в пикселях хранится в переменной rlist.iSSizeX, а в rlist.iSSizeXDiv2 половина ширины. В качестве вектора направления луча выступает dirData, в который входит массив из двух элементов, отвечающих за X и Y координаты. Безусловно, это не единственный способ вычисления направлений векторов. С помощью этого способа можно задать максимум 180-градусный угол обзора. Так же можно эти вектора создать с помощью поворота единичного вектора на определенный угол, который зависит от угла обзора и количества векторов. Но в любом случае длина вектора направления должна быть равна единице. Теперь остается добавить различные объекты в сцену и проверить каждый луч на пересечение с ними. В прилагающемся примере есть два типа объектов: - отрезок; - окружность. Сначала нужно выделить память для массива, где хранятся информация о каждом объекте. Для этого была написана функция rcSetCount, которая принимает один параметр типа int.
Теперь можно создавать сами элементы сцены. В примере это можно сделать вызывая функции rcLine( для отрезка ) и rcCircle( для окружности ). Цвет для этих объектов можно задать, предварительно вызвав функцию crColor4b. Функция rcTrace является главной. В ней осуществляются проверки на пересечения лучей с объектами сцены, и построение трехмерного изображения. В каждой итерации цикла( в примере итераций 320 ) проверяется пересечения луча dirData[ номер_луча ] со всеми активными объектами сцены. Если пересечение произошло, то вычисляется расстояние от наблюдателя до точки пересечения. Так же существует некая переменная, в которой хранится глубина пиксела, в примере она fSaveDepth. В начале каждой итерации ее значение становится равным пределу видимости. Так вот, получившиеся расстояние до точки пересечения сравнивается со значением в переменной fSaveDepth, если оно меньше, то в fSaveDepth записывается текущее расстояние. Так же в этом случае значение переменной iIntersect, становится равным номеру объекта, с которым произошло пересечение. Таким образом находится номер объекта и расстояние до точки пересечения для каждого луча. Зная расстояние и номер объекта, можно вычислить высоту вертикальной линии, которая будет выведена на экран. Это наипростейший случай, не предусматривающий даже затекстуривания. В примере высота линии вычисляется по формуле высота_экрана / расстояние_до_точки. Вот, в принципе, и весь минимум, который нужно знать о технологии RayCasting. Ниже я приведу краткий обзор функций класса clRayCast. rcInit( HDC hDc, RCINT iSizeX, RCINT iSizeY ); hDc - контекст окна, в которое будет выводится изображение. iSizeX, iSizeY - размеры изображения. Вызывается один раз. rcInitDir( void ); Вычисляются вектора направления лучей. Вызывается один раз. rcClear( RCINT rtiMode ); rtiMode - режим очистки. В примере есть лишь RC_COLOR_BUFFER_BIT. Очистка буфера, в котором хранится изображение. rcClearColor4b( RCUCHAR r, RCUCHAR g, RCUCHAR b, RCUCHAR a ); r, g, b, a - компоненты цвета. Значения от 0 до 255. Задает цвет, которым заполняется буфер изображения, при вызове rcClear. rcColor4b( RCUCHAR r, RCUCHAR g, RCUCHAR b, RCUCHAR a ); r, g, b, a - компоненты цвета. Значения от 0 до 255. Задает цвет, который является текущим, для объектов сцены, вызванных после него. rcSetCount( RCINT iCountShapes ); iCountShapes - максимальное количество объектов на сцену. Выделяет память под указанное количество объектов. Вызывается один раз. rcLine( RCFLOAT x0, RCFLOAT y0, RCFLOAT x1, RCFLOAT y1 ); x0, y0 - начало отрезка.. x1, y1 - конец отрезка. Добавляет линию к сцене. rcCircle( RCFLOAT x, RCFLOAT y, RCFLOAT r ); x, y - центр окружности. r - радиус окружности. Добавляет окружность к сцене. rcTrace( void ); Главная функция класса. В ней реализован RayCasting. rcSwapBuffers( void ); Вывод буфера изображения на окно. rcCamRot( RCFLOAT ang ); ang - угол поворота. Поворачивает все лучи на заданый угол. Многие считают RayCasting и RayTracing одной и той же технологией, но на самом деле RayCasting это технология с обратным ходом луча, а RayTracing с прямым. RayTracing позволяет добиться очень реалистичных эффектов, но он намного более требователен к железу, и поэтому создать движок, который бы быстро работал на RayTracing сейчас не представляется возможным. А теперь простенькая программа, с исходниками, демонстрирующая простейший RayCasting ( exe + src ) |
|