Как стать автором
Обновить

Комментарии 53

По-любому Асм под zx попробуйте, ещё большее удовольствие получите. По сути тот же самый си, только вместо каждой одной сишной команды вам надо будет писать стандартную последовательность из нескольких ассемблерных комманд.

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

Под Zx spectrum восновном писали на asm, посмотрите канал Alone Coder на ютуб

Благодарю, обязательно посмотрю

Не в основном, а всё путевое.
В детстве мечтал сделать свою Элиту. В итоге уже не на Спектруме, а на PC сделал проволочную графику на смеси ассемблера и паскаля. Дальше вращающихся фигур дело не пошло.

Ох, в детстве тоже был спектрум. А можно как-нибудь писать не на С, а на том самом Basic 48K?

Конечно, причем никакой дополнительный софт не нужен. Запускаете эмулятор и вперед )

Можно. Я писал. Была даже специальная книжка, в которой построчно разбирался код библиотеки, отдаленно напоминающей OpenGL. Только на реальном 128K это было все ну о-о-очень медленно, хотя по тем временам сделать надпись из трех букв «в объеме» и заставить ее крутиться на экране телевизора — это было просто «вау!» (Тем более, что по только что появившемуся местному телеканалу крутили рекламу одного банка, полностью состоящую из подобной «компьютерной графики»...) Потом еще пытался воспроизвести эту библиотеку на УКНЦ, но так и не доделал...

P.S.: В книжке было множество оговорок типа «а вот этот код на 16K работать не будет, потому что мало памяти»...

Только бэйсик работает в разы медленнее машинных кодов)

Как видно, в матрице присутствуют функции косинуса и синуса. Результат выполнения этих функций — вещественные числа.

Кстати, вы можете полностью отказаться от вычисления sin и cos рассчитав диапазон значений в момент запуска или просто создав массив. По примеру таблиц Брадиса только в электронном виде. Существенно ускорит работу.

И поддержу, истинное наслаждение получите только на asm ))

Идея хорошая, только памяти может не хватить... :-(

Если искусственно ввести ограничения на шаг угла например в 10 градусов, то будет всего 36 значений для каждой тригонометрической функции. При таком подходе это не должно съесть слишком много памяти, но конечно про плавное вращение можно забыть

Сделать интерполяцию — будет достаточно плавно. Правда, процессор будет кушать на интерполяцию.

Продолжая мысль, надо тогда и корни с логарифмами в таблицах хранить. А памяти — 48К, из которых 8К — видео, и еще пару килобайт съедают системные переменные. Остается 38К, в которые нужно засунуть программу, данные и таблицы синусов. А кто-то вообще на 16К сидел, имея в свободном распоряжении только 6К.

На «Спектруме» всегда приходилось делать выбор между «быстро» и «правильно»...

Рассчитать нужно всего 90 значений. sin считать напрямую, а cos с другой стороны таблицы. sin(1) = cos(90-1)

но я правда забыл уже это всё давно )) надо проверять

Да ладно, всё хватает. Во всех демках с ЗD-графикой использовались 256-байтные таблицы, как сейчас, не знаю, но вряд ли как то по-другому. Все значения были с точностью 1/256 и шли с угловым шагом 1/128 PI. Угол задаётся одним байтом, который является смещением(указателем-индексом) к таблице. Таблица имеет такой размер, чтобы не заморачиваться с отслеживанием выхода индекса за пределы таблицы, он всегда указывает правильно в таком случае. Погрешности таких вычислений начинают себя проявлять, когда объект находится близко от наблюдателя, вращение выглядит угловатым и нелинейным. В Elite это можно видеть во всей красе. Но для кубиков размером в 1/4 экрана такой точности достаточно. Иногда использовались 512-байтные таблицы, чтобы хранить более точные значения sin/cos (1/65536). Памяти хватало, а вычисления улетали в космос, по сравнению с бейсиком, конечно. Да и никому бы в голову не пришло вычислять тригонометрию в реалтайме.
прямо вот начал то же самое писать, но хорошо что комментарии перечитал

Спасибо за подсказку!

А ещё масштабирование малых чисел сделать не в 1000 раз умножением/делением, а в 1024 сдвигом на 10 бит.

Спасибо огромное за совет! Действительно, так должно быть быстрее. Похоже я слишком долго писал на Java под мощное железо, позабыл все приемы оптимизации )

Это называется вычисления с фиксированной точкой, можно в интернете найти информацию об этом. И удобнее их делать с выравниванием по байтам. Например в случае 16-и битной точности старший байт — целая часть (от -128 до +127), младший — дробная. Не надо будет заморачиваться со сдвигами и делениями — целая часть числа просто лежит в старшем байте.

Большое спасибо, обязательно почитаю!

В последнее время, когда писал для ардуины, у меня была куча флоат переменных, которые не умещались в памяти. Я их перевел в INT и использовал деление на 100, для 2 знаков после запятой, мне больше и не надо было. Так проще и быстрее. Но в спектруме я так не заморачивался, да и давно это было.
Был не плохой компилятор бейсика — ic48kv2 (integer compiler 48kb v2).
В школьные годы развлекался, сравнивал его производительность с программой написанной на чистом ассемблере и не заметил особой разницы. Работал с портами, если что.

Вспоминаю те 1992...1995 годы со Спектрумом с теплотой. Для учебы занимался программированием радиотехнических расчетов на встроенном Basic, а как кульминация разработок - взялся делать уже на Ассемблере, зачем-то, графический редактор картинки, размером 2 х 2 полных экрана.
И только когда практически получилось через недельку возни, после ощщщщущуния кайфа от достижения цели, я вдруг понял, что ..... нахрен этот редактор никому и низачем не нужен :)

Знакомые ощущения ) Когда то пытался писать под андроид, сначала думаешь что сделал обалденную штуку, и ни у кого такой нет, а потом количество скачиваний в Play Маркете говорит, что ее нет потому что она особо никому и не нужна )

Эх, я тоже свой графический редактор делал на закате платформы (с амбициозным названием "Graphic Station"), 1999 год, понес на местный радиорынок на дискете и там его добавили в общий каталог, было приятно когда получил несколько конвертов от реальных людей с откликами. Интернета у меня не было тогда.

По части оптимизаций и тригонометрии - был у меня там один инструмент "поворот картинки на произвольный угол" вот его я долго оптимизировал по скорости, пока уже дальше некуда) Табличные вычисления конечно. До 3D графики я на спектруме не дошел.

Видимо память меня подводит. Но я точно писал этот купбин на бейские под спектрум, под советский клон ямахи Корвет ПК8000 и так же на паскале под 386 pc. И я не помню что бы тормозило. Кубик на бейсике вполне вращался. Писался штатными Line…

Уверен, мой код можно серьезно оптимизировать. Это моя первая программа под zx-spectrum, буду учиться писать лучше )

работают они только в режиме графики с низким разрешением 32х48

Формально у спектрума нет такого режима. У него вообще только один режим — 256x192. А 32x48 — это судя по всему эмуляция за счёт оперирования с областью памяти управляющей цветами "чернил" и "фона" для знакоместа (8x8 пикселей). Разрешение в знакоместах у спектрума — 32x24. Если закрасить верхнюю половину каждого знакоместа, то можно как раз эмулировать 32x48 путём изменения цвета "чернил" и "фона" для нужного знакоместа. В вашем видео это видно — горизонтальные линии в два раза тоньше вертикальных.


А при работе с реальными пикселями может оказаться выгоднее просто ещё раз "нарисовать" те-же самые линии фоновым цветом (технически сбросить биты пикселей в 0). Если область изображения и число линий не велико, то это может оказаться быстрее, чем обнулять весь "буфер экрана".
Ну или можно было стирать не весь экран, а только ту часть, которую занимало изображение. А ещё оптимальнее было бы "нарисовать" всё куда-то в отдельный буфер-спрайт, и потом его скопировать в буфер экрана, тем самым затирая изначальное изображение. Что бы не было "тиринга" надо синхронизировать этот процесс с "обратным ходом луча ЭЛТ" — для этого в спектруме использовалось специальное прерывание.


На ZX 128кб есть аппаратная "двойная буферизация" — ещё одна страница памяти, которую можно быстро включить для использования в качестве источника изображения на экране.


Когда у меня был в своё время клон спектрума, я тоже делал такие штуки. Но я использовал вычисления с фиксированной точкой (уже не помню с какой точностью, возможно 16 бит на целую часть + 16 на дробную). Синусы и косинусы хранил в заранее посчитанной "таблице" с шагом 1 градус. При таком низком разрешении экрана и не очень больших объектах — этого вполне хватало. Ну и кроме того вращал я всё с шагом в 1 градус, поэтому не надо было ничего интерполировать. Писал я всё на ассемблере.

Что-то как-то очень медленно вращает. Так и задумывалось?
Вообще, есть книжка «Динамическая графика». Там есть процедуры рисования 3D с отсечением невидимых граней.

Вы это серьёзно или тролите?
Это же 8-ми битный компьютер с частотой процессора 3.5 МГц. Кроме того, это так называемая проволочная графика (wireframe), а не растровая — тут нет ни каких граней, на отсечении которых можно сильно сэкономить. Максимальная экономия из-за "отсечения" тут будет заключаться только в том, что надо будет в лучшем случае нарисовать 4 линии вместо 16-и. И то это редкий случай, когда кубик "смотрит" прямо на нас одной из своих "граней".
Ну и как уже писали выше — надо писать на ассемблере и использовать разные трюки для ускорения.

Вообще-то, серьёзно. ;) Я со спектрумом сидел с 1994 до 2001 года и его возможности представляю очень хорошо. :) Та скорость анимации (1-2 кадра в секунду), которую автор тут показал для кубика без заливки слишком мала для спектрума.

Это же 8-ми битный компьютер с частотой процессора 3.5 МГц.


И? :)

Максимальная экономия из-за «отсечения» тут будет заключаться только в том, что надо будет в лучшем случае нарисовать 4 линии вместо 16-и. И то это редкий случай, когда кубик «смотрит» прямо на нас одной из своих «граней».


Поиграйте в Elite или Academy. Увидите, какая там экономия за счёт выбрасывания граней, нормаль которых смотрит от наблюдателя.



А так, был цикл статей на эту тему.
zxpress.ru/article.php?id=11745
zxpress.ru/article.php?id=11766

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

В данном случае проблема не в этом, а в "наивной" реализации, ещё и не на ассемблере с трюками. А то бы кубик вполне себе вертелся со скорость 50 fps.

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


Ну вот автор попробует и узнаете, замедлит или нет.

а в «наивной» реализации, ещё и не на ассемблере с трюками.


А это уже проблема автора статьи. ;) Статья обещает научить делать 3D для спектрума. Только вот автор в детстве с этим компьютером дел не имел, что там когда кто наделал не знает, а потому получилось не очень для тех, кто на этом компьютере в 90-е собаку съел.
Вот ещё, какая скорость достигается на 3.5 Мгц:
Можно покопаться в журналах Спекрофон и т.п. Там были готовые статьи и даже примеры 3D движка.
А вообще для создания 3D в спектруме используется очень много интересных приемов и оптимизаций.
синусы и косинусы наверняка очень медленно. Еще во время программирования под БК 0010 использовал таблицы значений

они нужны по-моему только для расчета матрицы поворота, 6 значений за кадр посчитать полиномом не особо муторно, а память на таблицы значений экономится

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

Я такое лет 15 назад под J2ME писал, там тоже вещественной нет арифметики. Только у меня камера была и текстуры. BSP по-моему не было, интерес иссяк. Текстурированный кубик примерно 64 пикселя на грань крутился на телефоне весьма бодро.

Для реализации перспективной проекции я использовал формулу, подсмотренную в книге М. Мозгового «Занимательное программирование», которую очень любил, когда начинал программировать на Delphi.

Спасибо, приятно :)

Ого! Мир тесен ) Рад познакомиться!

У меня еще такая книга хранится :)



Да-а-а!!! Я про нее выше писал! Сколько часов было убито с этой книжкой...

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

А еще на первых страничках есть исходники игры в червяка, за которым тоже было убито немало времени. :-)

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

Обратите внимание, что адрес, выделенный голубым, в каждом цикле вычисляется по 8 раз, вместо того, чтобы вычислить его один раз в начале тела цикла. Причем для такого процессора как Z80 это выражение довольно тяжелое по времени выполнения, так как у него каждая команда адресации минимум занимает 4 такта (максимум 21), а в этом выражении их скорее всего 4 (трудно сказать в какой именно код генерит компилятор). Далее можно один раз вычислить указатель на ".src" и ".dst", чтобы не вычислять их по 4 раза каждый.

SCREEN_DEPTH равен 50 как я посмотрел определение, но если его сделать 64, то можно заменить умножение (которого нет на Z80 и вы зависите от реализации библиотеки) на сдвиг (который в Z80 есть). В этой статье разобраны реализации умножения и деления на Z80, с анализом времени выполнения, и эти операции занимают от 250 до 1000 тактов процессора, в то время как сдвиг занимает 8 тактов (без учета затрат на загрузку операндов). Если глубина 64 не подходит, и надо именно 50, то умножение на 50 можно сделать так:

X*50 = X*32+X*16+X*2 = X<<5 + X<<4 + X<<1 (примерно в 10 раз быстрее умножения)

И совершенно верно здесь уже написали в комментариях, что нужно применить таблицы для косинусов и синусов. В идеале еще избавится от деления - можно также попробовать через таблицу предварительно вычисленных значений (у вас ведь глубина принимает всего 50 целочисленых значений).

Аналогично функция processModel: "model3d->processedVerticies[i]" и "model3d->vertices[i]" вычисляются по шесть раз в каждой итерации цикла.

Аналогично цикл внутри функции multMatrix можно переписать, развернув внутренние циклы, и устранив повторные вычисление ссылки на строку матрицы во внутреннем цикле (пишу на условном языке без соблюдения синтаксиса С, просто чтобы смысл идеи показать):

for (int i = 0; i < 3; i++) {
		Matrix_I = matrix->values[i];
    res[i] = ((Matrix_I[0] * column[0]) >> 11) + ((Matrix_I[1] * column[1]) >> 11) + ((Matrix_I[2] * column[2]) >> 11);
    }
}

ROUND_COEFF нужно сделать 2048, вместо 2000 и все умножения и деления заменить на сдвиги.

Очень хорошая книга по оптимизации графики на компьютерах 90-х годов: Секреты программирования игр

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

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

Класный результат! Очень прятно видеть что кто-то делат программы для спеки. :)
Для отсечения невидины полигонов использовать нормали и правда дороговато для такого ПК. Можно определять frond face или back face по порядку верщин в экранных координатах. Если вершины треугольника идут по часовой стрелке, то это видиный полигон и наоборот. Для этого помимо данных вершин нужно иметь данне полигонов. их можно хранить как индексы вершин. Их же можно использовать как офсеты для расчета адресов в памяти где храняться координаты вершин.
По поводу очистки экрана. Мне кажется что стирать экран в твоем случае быстрее всего стековыми операциями. Но это не точно. Нужно тестить. До сих пор безгранично обажаю асемблер z80. Ох уж эти недокументированные операции. Прям кладезь одном префиксе. Надеюсь современные компиляторы их используют.
Для отсечения невидины полигонов использовать нормали и правда дороговато для такого ПК. Можно определять frond face или back face по порядку верщин в экранных координатах.


Эта ориентация граней и означает, что нормаль будет от наблюдателя или к наблюдателю. Визуально меняется порядок вершин в проекции. Чтобы такие грани отсечь, используется свойство определителя матрицы, как ориентированной (зависящей от направления обхода) площади (объёма и т.д. в зависимости от размерности) фигуры. Вот тут, страница 165.

Мне кажется что стирать экран в твоем случае быстрее всего стековыми операциями. Но это не точно.


Вот это — точно.

На сях бесполезно. Без асма делать нечего. Даже в 2D. При этом он очень прост. Я в 3D на нем не лез, чутка в демосцену. В основном занимался геймдевом. И все же очень упоротой оптимизации, код генерации, предварительных расчётов делать там нечего. Все 3D строится строго на предсгенерированных таблицах для тригонометрии. Вылизанных до основания функциях умножения и деления.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории