Из журнала Inferno Guide #5, Рязань/Самара, Май 2004 (в текст внесены исправления из Errata IG#7) Внимание: функции sin, cos и tan в статье подразумевают значения в градусах на входе и выходе. RAYCASTING - сделай себе немного DOOM'a. Shiru Otaku Давно, очень давно я мечтаю о том, что кто-нибудь, когда-нибудь сделает-таки на Speccy полноценную игрушку жанра 3d action - не Doom, конечно, или там Duke, но хотя- бы аналог Wolf3d. На данный момент уже су- ществуют очень неплохие попытки - Wolf3d demo (by Alone Coder), Citadel. Ред.: Движок игры Citadel основан не на рейкастинге,поэтому её мы больше упоминать не будем. Удивляет лишь, почему их (попыток) так мало по сравнению с количеством пишущей братии. Может быть, дело лишь в недостатке знаний (в своё время я тоже горел идеей написать что-то подобное, но не хватило именно знаний - а сейчас не хватает жела- ния)? Руководствуясь этим предположением, я решил накатать статейку, описывающую ме- тод, использованный в оригинальном Wolf3d by ID Software, а также десятках подобных игрушек того времени. Метод называется raycasting (не нужно путать с raytracing- 'ом) и ранее использовался для быстрой от- рисовки псевдотрёхмерного пространства в игрушках жанра 3d action. На текущий мо- мент считается, что этот метод давно уста- рел из-за налагаемых им ограничений на сложность отображаемого пространства, од- нако он и по сей день применяется на ма- шинах с ограниченной мощностью процессора - типа Palm Pilot, GameBoy Advance. Думаю, что именно этот метод вполне пригоден для реализации реально играбельного аналога Wolf3d и на Speccy (что доказывает собой, например, Wolf3d demo - там используется один из частных случаев рейкастинга). Я опишу метод рейкастинга в самом прос- том виде (стены одной высоты с текстурой, только под прямым углом друг к другу,пол и потолок без текстур).Не буду давать какие- либо конкретные решения - только общие принципы,алгоритмы,формулы. Конкретные ре- шения зависят уже от кодера, реализующего эти алгоритмы - чем оптимальнее у него вы- йдет код, тем лучше будет результат (а со- вершенству нет предела, даёшь 15fps на 3.5 МГц:) Сразу предупреждаю - как и все моро- чки с 3d-графикой,всё нижеописанное потре- бует некоторых знаний - теоремы Пифагора и прочей математики на уровне средней школы. ЧТО ТАКОЕ RAYCASTING? Рейкастинг - это метод преобразования ограниченной формы данных (очень простая карта этажа) в трёхмерную проекцию с помо- щью трассировки лучей из точки обзора в объём обзора. Это не я так умно выражаюсь, просто в книжке вычитал:) В общем,нижесле- дующий рисунок, думается, объяснит всё бо- лее понятно:
Надо заметить, что применение рейкасти- нга не ограничивается только построением такой проекции - его можно применять, нап- ример при построении воксельных поверхнос- тей (а-ля фон в Commanche на РС). Опреде- ляющим свойством рейкастинга является то, что лучи трассируются из точки обзора в объём - в отличие от рейтрейсинга, где лу- чи трассируются от объектов сцены к точке обзора. Также, если в рейтрейсинге лучи трассируются для каждой точки экрана, то в рейкастинге стены всегда перпендикулярны полу, и поэтому есть возможность трассиро- вать всего один луч для целого столбца эк- рана - именно поэтому рейкастинг и являет- ся наиболее быстрым методом построения трёхмерной проекции. Ред.: Если сравнивать по скорости все возможные применения псевдотрёхмерности,то быстрее рейкастинка окажутся два метода визуализации поверхности дороги для авто- гонок: построчный (обычный для ZX) и плос- костной проективный. 2.5D ИЛИ ОГРАНИЧЕНИЯ RAYCASTING'a. Преимущества рейкастинга в высокой ско- рости отрисовки пространства, но за эту скорость приходится платить - платить си- льными ограничениями и упрощениями отобра- жаемого мира. Его даже нельзя назвать по- настоящему трёхмерным - ведь на деле это всего лишь трёхмерная проекция обычной плоской карты. В оригинале метод рейкасти- нга не позволяет делать разноуровневый пол и разновысотные стены. На самом деле это всё же возможно - см. Doom или Duke3d (ма- ксимум возможностей рейкастинга),но,понят- ное дело, это требует уже не таких малень- ких вычислительных ресурсов. С другой сто- роны, во времена 286-х процессоров, когда трёхмерная графика в реальном времени была возможна разве что на SGI, эти упрощения были незначительной ценой. Не будут они значительной ценой и для Speccy. Главное ограничение рейкастинга - стен- ки по отношению к полу/потолку должны быть только под прямым углом (иначе теряется возможность трассировки одного луча на столбец,и пропадёт всё преимущество в ско- рости). Также, обычно, стенки могут быть только двух видов - вертикальные и горизонтальные (если смотреть на карту сверху). Это огра- ничение обходимо, но усложняет и замедляет программу (поэтому диагональных стенок нет в том же Wolf3d). ЭТОТ КВАДРАТНЫЙ, КВАДРАТНЫЙ МИР... Итак, при рассмотрении принципов рейка- стинга мы будем руководствоваться следую- щими ограничениями нашего мира: - Стенки всегда под прямым углом относи- тельно пола. - Стенки представляют собой равносторон- ние кубики. - Пол всегда плоский (на одном уровне). Размер каждого куба мы примем за 64х64х x64 условных единицы. Разумеется,это может быть и любое другое число, являющееся сте- пенью двойки (это чтобы можно было вовсю использовать операции сдвига). Чем больше размер куба - тем больше будет упрощён- ность пространства (большие кубы), но выше скорость, и наоборот - при маленьких куби- ках процесс трассировки будет занимать бо- льше времени. Размер кубов стоит выбирать, основываясь на предполагаемом размере бу- дущих текстур - если они будут 32х32 (что наиболее логично для Speccy) - то и раз- мер кубов стоит выбрать 32х32х32, это уп- ростит расчёты. В общем, наш мир будет вы- глядеть примерно так:
ВНИМАНИЕ! Перед тем как продолжать, ну- жно определиться с очень важной вещью - системой координат. В дальнейших разьясне- ниях будет использоваться следующая систе- ма координат (картезианская):
То есть, X - горизонтальная ось,положи- тельные значения - справа; Y - вертикаль- ная, положительные значения - СНИЗУ. 0 градусов - справа, и далее ПРОТИВ часовой стрелки (сверху 90, слева 180, снизу 270). Это нужно учитывать при рассмотрении ниже- сказанного - иначе будут лишние глюки. Теперь далее. Определим ещё несколько вещей: - Угол обзора (поле зрения, Field of view, FOV), координаты игрока (точки обзо- ра). - Размеры projection plane (плана прое- кции - звучит коряво, я лучше буду исполь- зовать английский термин). - Зависимость между игроком и projection plane. Игрок видит то,что находится перед ним. Его поле зрения имеет определённый угол (FOV). Угол определяет, насколько широко игрок видит мир вокруг себя. Люди имеют угол обзора 90 и более градусов (Ред.: около 170° - можете проверить это сами, разведя руки и пошевелив пальцами), но для наилучшего вида на экране мы примем FOV за 60 градусов. Кстати, если менять FOV в ши- роких пределах (до 120 градусов) - можно получить занятные эффекты,наподобие "рыбь- его глаза" - как в Duke3d, когда Дюк попа- дает под уменьшитель. Ред.: Я предпочитаю угол в 90°. Настоя- щий угол обзора экрана всё равно не соот- ветствует ни одному из предложенных значе- ний - он меньше 40°, что, очевидно, непри- менимо в качестве FOV. Для перемещения игрока в пространстве он должен иметь три координаты - Y, X на двухмерной карте (если смотреть сверху), а также point of view (POV) -"точку зрения", хотя логичнее сказать - направление взгля- да,в градусах. Если POV равен 0 градусов - игрок смотрит вправо по карте (опять-таки, если смотреть на карту сверху). Вот эта картинка поясняет (если кто не понял), что такое FOV и POV, а также пока- зывает, куда будет смотреть и что увидит игрок, если его FOV = 60 градусам, а POV = = 45 градусам.
Последнее - нам нужно определить наш projection plane, то есть, грубо говоря, размеры в точках (не пикселей, т. к. на Speccy точками могут быть, например,чанки) нашего окна, куда мы будем отрисовывать проекцию. В доке,по мотивам которой я пишу эту статью, всё было рассчитано на станда- ртное разрешение VGA 320x200, поэтому, чтобы не запутаться самому,я оставлю имен- но такие размеры окна - 320 столбцов, каж- дый 200 пикселей в высоту (всё-таки это теоретический пример,а не практический),но для реальной задачи, конечно,нужно выбрать более разумные размеры - например, 64*48 точек. Нужно учитывать, что чем шире pro- jection plane, тем больше лучей придётся трассировать, а значит,тем медленнее будет работать программа. Высота же projection plane не влияет на скорость трассировки лучей,но может повлиять на скорость работы процедуры масштабирования столбца. Коорди- наты точек на projection plane обычны - начало координат (0,0) находится в верхнем левом углу. "CAST A RAY". Теперь,когда все исходные параметры оп- ределены,можно приступить к описанию прин- ципа построения проекции. Для начала небо- льшой,но симпатичный чертёжик(чёрт-ёжик;):
Из этого всего нам известно: - Размер projection plane равен 320x200 точек. - Центр projection plane находится в 160,100. - Расстояние до projection plane = 277 единиц (половина ширины projection plane / тангенс половины FOV). - Угол между соседними лучами = 60/320 градусов (60 градусов FOV / ширина projec- tion plane). Эти значения являются константами, они не будут меняться далее (разве что вы не захотите сделать изменяемый в realtime FOV :), так что если непонятно, откуда берутся те или иные значения - можно не ломать го- лову, а просто использовать их. Угол между соседними лучами - всё равно что угол меж- ду соседними столбцами, мы будем использо- вать это значение для перехода от столбца к столбцу при трассировке. Расстояние до projection plane понадобится нам для нор- мального масштабирования получаемой карти- нки. Нашу проекцию (отображаемые стенки) мо- жно рассматривать как 320 (ширина projec- tion plane) вертикальных столбцов. Для ка- ждого из них мы будем трассировать отдель- ный луч. Крайнему левому столбцу будет со- ответстовать угол POV-(FOV/2), для крайне- го правого POV+(FOV/2). От столбца к стол- бцу можно переходить,добавляя к начальному значению POV-(FOV/2) на каждой итерации FOV/320. Итак, алгоритм построения проекции та- ков: 1. Вычитаем из текущего POV половину FOV - это наш угол текущего луча. 2. Начиная от столбца 0 (левого): - "cast a ray", как говорят америкосы ;) - испускаем луч.Луч исходит из ко- ординат игрока в направлении POV. - идём лучом по клеткам,пока не попа- дём в клетку со стенкой. - запоминаем длину полученного луча. 3. Добавляем к углу текущего луча FOV/320. 4. И так все столбцы (320 раз в нашем слу- чае). Пока всё просто, но есть два важных мо- мента.Первый - необходимо быть уверенным в том, что луч рано или поздно встретит сте- нку.То есть,как минимум вся карта по пери- метру должна быть окантована стеной. Также не стоит забывать, что чем дальше приходи- тся трассировать луч - тем медленнее пост- роение проекции в целом. Поэтому стоит ог- раничить дальность обзора разумными преде- лами (подобрать на глазок). Ред.: Это ограничение слабо прибавит скорости в Wolf-подобных играх - хорошо спланированные лабиринты не содержат за- лов,в которых от одной стены не видно про- тивоположную. Ограничение поможет в играх с открытым ландшафтом. Второй важный момент - нам не нужно проходить каждую точку каждой клетки (точ- ность необходима - иначе не построить про- екцию),достаточно проверять границы клеток - см. рисунок:
Нас интересуют только точки A,B,C,D,E,F Мы должны пройти через них поочерёдно,про- веряя на наличие/отсутствие стены в клет- ке. Наиболее простой (но не самый быстрый) способ - трассировать луч дважды, проверяя сначала только горизонтальные, а затем то- лько вертикальные пересечения; запомнить оба расстояния до найденных стен,и выбрать из них ближайшее.Разумеется,проверки можно и объединить, но это сильно усложнит алго- ритм трассировки луча (и, может быть, при- бавит скорость, а может, и нет ;). Я опишу простой, раздельный способ. Вся фишка в том, что расстояния между всеми горизонтальными (и вертикальными) пересечениями одинаковы.Это можно заметить из следующего рисунка (слева для горизон- тальных, справа для вертикальных пересече- ний):
Назовём вертикальное расстояние до сле- дующей точки Ya, горизонтальное Xa. Будем искать все горизонтальные пересечения. Ya найти легко - это высота клетки (64 в на- шем случае). Xa можно найти по формуле Xa= =64/tan(угол луча). Разумеется, для Speccy тангенс каждый раз считать накладно,но мо- жно посчитать табличку для всех возможных углов заранее. Для поиска вертикальных пе- ресечений Xa равен ширине клетки, Ya = 64* *tan(угол луча). Теперь более подробный алгоритм. ВНИМА- НИЕ! Помните про используемую систему ко- ординат! По оси Y положительные значения находятся СНИЗУ, отрицательные - СВЕРХУ! Ред.: Если вы напишете программу, но не угадаете этот знак (что, боюсь,произошло в этой статье;), попробуйте считать углы за- крученными не против,а ПО часовой стрелке. Трассировка для горизонтальных пересе- чений (координаты игрока Py,Px; координаты проверяемой точки Ay,Ax): 1. Находим первое ближайшее пересече- ние (точка А ). Если луч идёт вверх - Ay = int (Py / 64) * 64 - 1; если вниз - Ay = int (Py / 64) * 64 + 64. Ax находится после вычисления Ay: Ax = Px + (Py-Ay) / / tan(угол луча). 2. Находим Ya. Оно равно высоте клетки, то есть 64 в нашем случае. Если луч надо трассировать вверх - Ya должно быть отри- цательным, если вниз - положительным. 3. Находим Xa. Xa = 64/tan(угол луча). 4. Проверяем пересечение на наличие сте- нки. Для проверки нужно текущие координаты преобразовать в координаты клеток - то есть,разделить на размер клетки (сдвигом), в нашем случае проверить клетку в коорди- натах Ay/64, Ax/64. Если стенка есть - за- поминаем расстояние от Py,Px до Ay,Ax и прекращаем цикл. 5. Если стены нет - переходим к следую- щему пересечению. Ax=Ax+Xa, Ay=Ay+Ya, и так до победного. Трассировка для вертикальных пересече- ний не сильно отличается (координаты игро- ка Py,Px; координаты проверяемой точки By, Bx): 1. Находим первое ближайшее пересече- ние (точка B ). Если луч идёт вправо - Bx = int (Px / 64) * 64 + 64; если влево - Bx = int (Px / 64) * 64 - 1. Далее, By = = Py + (Px - Bx) * tan(угол луча). 2. Находим Xa. Оно равно ширине клетки, то есть 64 в нашем случае. Если луч надо трассировать влево - Xa должно быть отри- цательным,если вправо - положительным. 3. Находим Ya. Ya = 64 * tan(угол луча). 4. Проверяем пересечение на наличие сте- нки. Если стенка есть - запоминаем рассто- яние от Py,Px до By,Bx и прекращаем цикл. 5. Если стены нет - переходим к следую- щему пересечению. Bx = Bx+Xa, By = By+Ya. После двух трассировок выбираем ту точ- ку, которая ближе (сравнив найденные рас- стояния),для неё и будем рисовать столбец. В приведённом на картинках примере это бу- дет точка E. Нужно бы подробнее рассказать о том,как найти расстояние от игрока до стенки. Есть несколько способов. Расскажу о двух. Недо- статок их в том, что в одном нужно считать квадратный корень,а в другом - синус и ко- синус. Можно и просто посчитать расстояние (X=X2-X1, Y=Y2-Y1), но будет сложнее боро- ться с искажениями (о них - в следующем разделе). Первый способ - расстояние от Px, Py до конечной точки Ex, Ey = sqrt ( (Px-Ex)^2 + + (Py-Ey)^2 ). Этот способ неудобен тем, что много медленных операций - два умноже- ния и взятие квадратного корня. Второй способ - расстояние = abs (Px - Ex)/cos(a) = abs (Py - Ey)/sin(a), где a = угол луча. Этот способ удобнее тем,что та- бличку косинуса и синуса можно посчитать заранее,а из медленных операций - одно де- ление. После того, как мы нашли расстояние до столбцов, можно уже и приступить к отрисо- вке проекции. ДОРАБОТКА НАПИЛЬНИКОМ. Но тут есть одно "но".Если прямо сейчас нарисовать столбцы (без текстуры) по най- денным расстояниям до них, то можно сильно обломаться - вместо, например, прямой сте- нки мы увидим нечто наподобие этого:
Ред.: Вспомните игру Doom Mania. Это потому, что мы не учли особенность экрана монитора. Дело в том,что он,в отли- чие от человеческого глаза,не сферический, а плоский. И поэтому, если расстояние от точки обзора до прямой стены посередине одно,то расстояние от точки обзора до края зоны обзора - другое. Значит, нужно откор- ректировать значения расстояний до столб- цов, учитывая этот факт. Искажение убрать несложно по такой фор- муле: правильное расстояние = искажённое расстояние * cos(a), где a - угол текущего луча. Значение cos(a) можно посчитать за- ранее для каждого из столбцов,значит,у нас получится табличка в 320 чисел (угол a ме- няется от 30 до -30 градусов - помните про нашу систему координат!), и все найденные расстояния нужно будет умножить на значе- ния этой таблички. Ред.: Не забудьте, что этим мы убираем только вертикальные, но не горизонтальные искажения.Текстуры на краях экрана по сра- внению с центром будут сжаты по горизонта- ли. В Wolf demo на ZX этого нет, поскольку применён не арифметический метод, а скани- рование "напролом", масштабы скорректиро- ваны уже в самом "веере" верторов сканиро- вания (периферия экрана сканируется более крупными шагами, концы векторов сканирова- ния, собранные в один пучок, указывают на точки, РАВНОМЕРНО расположенные НА ОДНОЙ ЛИНИИ). Вообще,для Speccy эти умножения - боль- шая потеря скорости, и поэтому стоит попы- таться убрать или убавить искажения каким- либо другим способом.Ну да это уже на ваше усмотрение:). А теперь, наконец-то... СТРОИМ СТЕНКИ. Да, дело осталось за малым - нарисовать на экране столбы нужной высоты (которую можно узнать из найденного расстояния для каждого столбца). Ну, так и нарисуем. Пока можно нарисовать просто заполненные одним цветом столбики, чтобы увидеть коридоры, позже добавим текстуру. Высота столбца находится так: высота текстуры / расстояние до столбца *277 (на- деюсь, вы помните,откуда взялось это 277 - я упоминал об этом вначале). Верхняя точка на экране, с которой нужно отрисовывать столбец, находится ещё проще - высота pro- jection plane / 2 минус высота столбца /2. Можно рисовать. Но надо отметить один подводный камень. Если сделать всё так,как написано выше, то можно будет наблюдать небольшие искажения вроде бы прямых стенок; а если натянуть на них текстуру (см.далее) - то иногда на ро- вных линиях будут заметны зубцы - чем бли- же к стенке,тем они больше.Честно говоря,я не разобрался, откуда они возникают,но из- бавиться от этого эффекта просто, если ок- руглить высоту столбца до чётного числа (была 31 - станет 30, и т.д.). Это почти полностью уберёт эффект зубцов (немного останется, но это уже из-за ошибок округ- ления). Ред.: Действительно, ступеньки можно убить округлением высоты до кратной 2. Hо это понижает точность масштабирования! По- этому лучше масштабировать каждый столбик от середины - могут быть даже дробные вы- соты. КЛЕИМ ОБОИ (ТЕКСТУРЫ НА СТЕНАХ). Осталось совсем немного - натянуть на стенки текстуру.Это очень просто.Нам нужно только узнать, какой из столбцов текстуры нужно выводить для каждого из столбцов эк- рана, после чего вывести его с помощью ка- кой-либо процедуры масштабирования (не бу- ду рассказывать, как написать такую проце- дуру - это гораздо проще,чем алгоритм рей- кастинга,так что справитесь сами).А узнать совсем несложно - берём координату Y, если был найден столбец вертикальной стенки,или X, если столбец горизонтальной, урезаем его,к примеру, AND'ом, оставив последние 6 бит (размер клетки 64) - это и будет необ- ходимое смещение в текстуре. Останется вы- вести этот столбец текстуры, растянув его по вертикали до требуемых размеров.
ПЕРВЫЕ ШАГИ В КВАДРАТНОМ МИРЕ. Ну вот, теперь у нас есть замечательные стенки, с текстурой,без искажений. Но пока мы смотрим только из одной точки, не имея возможности побродить по окрестностям.Надо ещё сделать нормальное перемещение игрока в пространстве.Нормальное - это когда кла- виши влево-вправо разворачивают игрока во- круг своей оси, а вперёд-назад позволяют передвигаться в выбранном направлении. Да ещё неплохо бы не уподобляться привидению, и всё-таки не ходить сквозь стенки.Это то- же несложно. Мы имеем координаты игрока Px,Py и угол Pa (POV). Нам понадобятся ещё два парамет- ра - скорость передвижения Pspd и скорость разворота Prot. На клавиши влево-вправо нужно повесить изменение Pa - прибавлять и убавлять его на значение Prot. При этом неплохо бы следить, чтобы Pa был в диапа- зоне 0-359. Для движения вперёд (клавиша вверх) нам нужно найти следующую точку на векторе Pa, на расстоянии Pspd от текущей. Находим: Px = Px+cos(Pa)*Pspd; Py = Py+sin(Pa)*Pspd. Для движения назад нужно, соответствен- но,вычитать,а не прибавлять значения.Чтобы не проходить сквозь стенки,нужно проверять новую найденную точку на предмет наличия стены,и если там стенка - то или просто не идти, или - что получше - пробовать умень- шать Pspd, пока не найдётся свободная точ- ка. Ред.: Рекомендуется считать стеной мес- та, отстоящие от реальной стены ближе чем на параметр "радиус персонажа". Это связа- но с тем, что стенка на нулевом расстоянии видна в бесконечном (во всяком случае, бо- льшом и неэстетичном) увеличении. Можно также добавить инерцию при ходьбе и развороте - ну да это уже не мне вас учить:). ПРИСЯДЕМ НА ДОРОЖКУ. Можно дополнить наш движок такими воз- можностями, как эффект приседания или пры- жка игрока. Для этого всего-навсего нужно сместить центр projection plane при расчё- те верхней точки выводимого столбца - если уменьшим, то присядем, увеличим - подпрыг- нем. Очень просто. Не менее просто и изобразить взгляд в пол или потолок.Для этого при расчёте вер- хней точки нужно отнимать от половины вы- соты projection plane не половину высоты столбца, а треть или четверть - в общем, экспериментируйте на здоровье. И ОБ ОБЪЕКТАХ. В итоге получилась почти игра - есть трёхмерный лабиринт, по нему можно побро- дить. Но кроме стенок ничего нет, а это не очень интересно. Нужны враги,МНОГО врагов, и какой-нибудь пистолетик:) Да и двери не помешали бы. Только вот я очень утомился писать эту статью,поэтому коротко расскажу общие идеи, а вы уж додумывайте сами. Двери а-ля оригинальный Wolf3d. Их мож- но сделать следующим образом. Если при трассировке луч попал в клетку с дверью - эту клетку мы будем трассировать поточеч- но. Если в какой-то момент луч пересечёт эту клетку посередине - в месте, где нахо- дится дверь - то эту точку мы и берём как найденное расстояние. Дверь при этом будет как бы в проёме в половину ширины стены (сама дверь будет, конечно, "картонной" - толщиной в пиксель, но это незаметно, ведь не часто прходится смотреть дверям в торец ;). Объекты - враги, всякие ночные вазы и прочая утварь - можно выводить, используя обычные методы 3D-графики. То есть хранить их координаты X, Y и при повороте игрока вокруг оси вращать все эти координаты; при выводе использовать обычное проецирование 3D-точки на плоскость (3D координата Х = Х в нашем пространстве, Y=0, Z = координате X нашего уровня). Главное выводить их тоже по вертикальным столбцам, учитывая рассто- яние до стенки - если стенка ближе,чем ка- кой-то из столбцов объекта - выводить сте- нку,дальше - выводить сначала стенку,потом поверх неё столбец объекта. Если есть такая возможность (при выводе экрана с помощью чанков, например) - стоит использовать изменение яркости столбцов в зависимости от расстояния - может улучшить разборчивость картинки (чем дальше стенка, тем "темнее"). В ЗАКЛЮЧЕНИЕ. Ну, всё, я вас просветил - теперь айда все клепать Вульфы и Думы!:) Хочу также заметить, что реально быстрый движок можно сделать, только если хорошо подумать голо- вой.Написать трассировку лучей можно деся- тком способов, есть множество возможностей максимально уменьшить количество необходи- мых realtime-вычислений,упростить алгоритм и т.д.,и т.п. Так что всё зависит от вас:) За кадром осталось немало тем,например, отрисовка с помощью рейкастинга текстур на полу и потолке - я не стал описывать этот процесс, т. к. для Speccy он явно неприем- лем из-за скорости - даже на РС это рабо- тает не очень быстро (потому в оригиналь- ном Wolf3d и нет текстур на полу/потолках) Ред.: затраты на пол и потолок больше затрат на масштабирование стен - которое само занимает около половины всего времени работы алгоритма. То есть,с полом и потол- ком медленнее раза в два. Тем не менее, если что-то интересно - нужная информация без проблем находится в Сети. Например,могу порекомендовать раздел из PC-GPE "Doom technique", либо упомяну- тую строкой ниже доку. Всё вышенаписанное основано на доке "Ray-Casting Tutorial" by F. Permadi, (C) 1996-01 (англоязычный оригинал можно найти на http://permadi.com/tutorial/raycast/ ), по которой я учился сам,и является вольным её переводом/пересказом своими словами, в сильно сокращённом варианте и с моими не- сомненно очень ценными дополнениями;) Shiru Otaku/ANGEL2 (shiru#mail.ru) Raycasting note Статья Shiru Otaku про рейкастинг была изъята из неопубликованного материала Adventurer #14. В соответствии с традиция- ми нашего издания (а точнее,его Guide-час- ти ;), она также снабжена моими ремарками. Разумеется, моё мнение не является истиной в последней инстанции. Кстати,данный мате- риал, достаточно сдобренный личным непони- манием со стороны лекторов, преподаётся на курсе "Компьютерной графики" в районе третьего курса специальности "Программное обеспечение чего-то чего-то и чего-то че- го-то". Спектрумистам учиться в ВУЗе по данной специальности не советую. Изначально в статье содержалось мнение, что персонаж нельзя вращать по оси, перпе- ндикулярной экрану. Вот так:
Однако я со своей стороны полагаю, что это возможно. Для этого нужно строить кар- тинку, будто герой не вращался около этой оси, а потом переносить картинку на экран в повёрнутом виде. Конечно, это редко может потребоваться, но вдруг? В demo, например... Стены под 45° (т.е. с СВ на ЮЗ или с СЗ на ЮВ ) можно реализовать и в стандартном рейкастинге,и в моём варианте,где сканиро- вание сплошное. Во втором случае это отни- ет не так много процессорного времени, но это число теоретически.Поскольку цикл ска- нирования станет очень сложным. При входе в каждый кубик нужно проверить,есть ли там диагональная стенка, и если да, то какая - в соответствии с её типом должен произойти переход на нужный вариант внутреннего цик- ла. При обработке диагональных вариантов одна из координат должна быть искажена: X=X+Y, или X=X-Y, или Y=X+Y, или Y=X-Y, в зависимости от типа стенки и направления входа в кубик. То же нужно сделать с пере- менными, содержащими шаг. Тогда цикл будет работать как обычный, т.е.сложение с выхо- дом по переполнению. Собственно,и в стандартном рейкастинге, если тип кубика со стенкой знать заранее, скорость упадёт вовсе не в 2 раза. Но ал- горитм, видимо, ещё мудрёнее. Alone Coder