7 KiB
7 KiB
Архитектура и математический пайплайн EBLAN ENGINE 3D
1. Системы координат и единицы измерения
Мировое пространство (World Space)
- Мир разделен на клетки сетки размером 64x64 единицы (
CELL_SIZE equ 64). - Координаты игрока
playerXиplayerYхранятся в формате 16.16 signed fixed point:- Целая часть =
координата / 64(индекс клетки в сеткеgameMap). - Дробная часть = смещение внутри клетки.
- Целая часть =
- Карта
gameMapимеет размер 32x32 клетки (всего 1024 байта). Индекс клетки вычисляется через сдвиг:index = (mapY << 5) + mapX.
Углы и тригонометрия (Angles & Trig)
- Полная окружность (360°) разделена на 512 дискретных угловых шагов (
ANGLE_512):0= Восток (вправо, +X)128= Юг (вниз, +Y)256= Запад (влево, -X)384= Север (вверх, -Y)
- Угол всегда маскируется:
and eax, 511(исключает переполнение и ветвления). - Таблицы
tableSinиtableCosпредвычисляются на старте через математический сопроцессор FPU:rad = (angle * PI) / 256 tableCos[angle] = round(cos(rad) * 65536) tableSin[angle] = round(sin(rad) * 65536)
2. Конвейер лучевого рендеринга (2.5D Raycasting)
Каждый кадр для экрана шириной 320 пикселей выпускается ровно 320 лучей (по 1 лучу на каждый вертикальный столбец экрана):
Левый край экрана Правый край экрана
(screenX = 0) Игрок (screenX = 319)
\ | /
\ | /
\ Угол обзора (FOV = 96) /
\ | /
\ v /
Алгоритм на столбец:
- Вычисление угла луча:
\text{rayAngle} = \left(\text{playerAngle} - \frac{\text{FOV}}{2} + \frac{\text{screenX} \cdot \text{FOV}}{320}\right) \ \& \ 511 - Трассировка по сетке (DDA - Digital Differential Analysis):
- Вычисляются шаг сетки (
stepX = \pm 1,stepY = \pm 1) и расстояния до следующих линий сетки (sideDistX,sideDistY). - В цикле луч шагает от клетки к клетке, пока не встретит твердую ячейку (
IsSolidCell). - Флаг
sideHitфиксирует, была ли пересечена вертикальная грань (X) или горизонтальная (Y).
- Вычисляются шаг сетки (
- Устранение дисторсии «рыбий глаз»:
- Расстояние по лучу умножается на косинус разницы между углом луча и углом камеры:
\text{dist}_{\text{perp}} = \text{dist} \cdot \cos(\text{rayAngle} - \text{playerAngle})
- Расстояние по лучу умножается на косинус разницы между углом луча и углом камеры:
- Проекция высоты стены:
\text{lineHeight} = \frac{\text{CELL\_SIZE} \cdot \text{PROJ\_DIST}}{\text{dist}_{\text{perp}}}- Центр проекции — середина экрана (
Y = 120). - Верхняя граница:
\text{drawStart} = 120 - \frac{\text{lineHeight}}{2}. - Нижняя граница:
\text{drawEnd} = 120 + \frac{\text{lineHeight}}{2}.
- Центр проекции — середина экрана (
- Текстурирование и затенение:
- Вычисляется горизонтальная координата текстуры
U \in [0, 127]. - Текстура интерполируется по вертикали с шагом
\text{texStep} = \frac{128 \ll 16}{\text{lineHeight}}. - $Y$-грани затемняются на 50% (
>> 1) для создания ощущения трехмерного объема. - Далекие стены плавно уходят в туман (половинная яркость от 350 единиц, почти черный от 700).
- Вычисляется горизонтальная координата текстуры
3. Билборд-спрайты (Sprites)
- Спрайты хранят позицию в сырых мировых единицах (1 клетка = 64 единицы, как
playerX/playerY). Перед отрисовкой все спрайты сортируются по убыванию квадрата расстояния (алгоритм художника, insertion sort поspriteOrder). - Позиция каждого спрайта переводится в систему координат камеры:
X_{\text{cam}} = \Delta X \cdot \cos(-\text{ang}) - \Delta Y \cdot \sin(-\text{ang})Y_{\text{cam}} = \Delta X \cdot \sin(-\text{ang}) + \Delta Y \cdot \cos(-\text{ang}) - Если
Y_{\text{cam}} > 8(спрайт находится перед камерой):- Проекция на экран:
\text{screenX} = \frac{X_{\text{cam}} \cdot \text{PROJ\_DIST}}{Y_{\text{cam}}} + 160. - Размер:
\text{size} = \frac{64 \cdot \text{PROJ\_DIST}}{Y_{\text{cam}}} \cdot \text{scale}(scale — 16.16 fixed).
- Проекция на экран:
- Спрайт «стоит на полу»: нижняя граница = горизонт + size/2 (как низ стены на той же глубине), верхняя = низ − size.
- Отрисовка вертикальных текстурированных полос с проверкой буфера глубины
depthBuffer[stripeX]: если спрайт ближе стены, пиксели выводятся вscreenBuffer. Тексели00FF00FFh(маджента) прозрачны. Применяется тот же дистанционный туман, что и у стен.
4. 2D Буфер кадра и вывод на экран
screenBuffer— плоский массив в оперативной памяти размером320 \times 240 \times 4 = 307\,200байт.- Формат пикселя — 32-битный BGRA (
00RRGGBBh). - Передача кадра в окно Windows производится аппаратным вызовом GDI:
invoke StretchDIBits, hEngineDC, 0, 0, 1280, 960, ; Область окна назначения 0, 0, 320, 240, ; Область исходного буфера OFFSET screenBuffer, OFFSET bmiEngine, DIB_RGB_COLORS, SRCCOPY - Обеспечивает честное ретро-разрешение и пиксельный вид с нулевой нагрузкой на видеокарту.