Привет всем! Рождество закончилось доели последний шоколад, так что пришло время заняться 8-ой частью Демо серий Asphixia. Эта часть в основном посвящена 3D, но также включает некоторые вопросы по потимизации. Если вы уже "3D-отец", то можете смело пропускать текст, просто взгляните на тренировочную программу и идите спать дальше, потому что я собираюсь объяснить детально как эта вещь работает. Если вы хотите законтачить со мной или с нашей командой, то есть много путей достижения этого: 1) напишите по приветному мылу сообщение на ..... 2) ... 7) --> другие способы... :) Если вы представляете компанию или BBS и хотите чтобы ASPHYXIA сделала для вас демо, то пишите на мыло - обсудим... Если написали демо - присылайте мне, мы чувтсвуем себя очень одинокими и хотим встретить соратников. Оптимизация. Прежде чем мы начнем говорить о 3D, я хочу заметить, что многие процедуры и возможно ваши собственные могут быть значительно ускорены при помощи небольшой оптимизации. Однако вы должны понимать, ЧТО нужно оптимизировать... Перекодировка процедуры, которая только лишь вызывается в начале программы - при инициализации, на тяжело кодируемый ассемблер покажет какой вы крутой программер, но абсолютно не ускорит программу. Кое-что, что вызывается часто в кадре, нуждается в том, чтобы быть максимально быстрым. Наиболее применяемая процедура PutPixel. Вот эта процедура, которую я дал на прошлой неделе: Procedure Putpixel (X,Y : Integer; Col : Byte; where:word); BEGIN Asm push ds { 14 тактов } push es { 14 } mov ax,[where] { 8 } mov es,ax { 2 } mov bx,[X] { 8 } mov dx,[Y] { 8 } push bx { 15 } mov bx, dx { 2 } mov dh, dl { 2 } xor dl, dl { 3 } shl bx, 1 { 2 } shl bx, 1 { 2 } shl bx, 1 { 2 } shl bx, 1 { 2 } shl bx, 1 { 2 } shl bx, 1 { 2 } add dx, bx { 3 } pop bx { 12 } add bx, dx { 3 } mov di, bx { 2 } xor al,al { 3 } mov ah, [Col] { 8 } mov es:[di],ah { 10 } pop es { 12 } pop ds { 12 } End; END; Всего = 153 такта Внимание: не принимайте мое обозначение тактов за абсолютную истину: возможно я сделал пару ошибок. Теперь, об оптимизации: Во-первых, если у вас включены инструкции 286-го проца, то вы можете заменить 6 shl,1 на Shl,6. Во-вторых, комилятор Паскаля автоматически сохраняет и восстанавливает регистр ES, так что две строки можно убрать. DS:[SI] не увеличивается в данной процедуре, так что ее тоже можно убрать. Также, вместо того, чтобы двигать Col в AH, мы можем записать его в AL и вызвать STOSB(ES:[DI]:=AL; Inc DI). Давайте взглянем на процедуру теперь: Procedure Putpixel (X,Y : Integer; Col : Byte; where:word); BEGIN Asm mov ax,[where] { 8 } mov es,ax { 2 } mov bx,[X] { 8 } mov dx,[Y] { 8 } push bx { 15 } mov bx, dx { 2 } mov dh, dl { 2 } xor dl, dl { 3 } shl bx, 6 { 8 } add dx, bx { 3 } pop bx { 12 } add bx, dx { 3 } mov di, bx { 2 } mov al, [Col] { 8 } stosb { 11 } End; END; Всего = 95 тактов Теперь, давайте запишем значение BX прямо в DL, таким образом избегая длительных команд push и pop. MOV, XOR DX могут быть заменены их эквивалентами: SHL DX,8 : Procedure Putpixel (X,Y : Integer; Col : Byte; where:word); assembler; asm mov ax,[where] { 8 } mov es,ax { 2 } mov bx,[X] { 8 } mov dx,[Y] { 8 } mov di,bx { 2 } mov bx, dx { 2 } shl dx, 8 { 8 } shl bx, 6 { 8 } add dx, bx { 3 } add di, dx { 3 } mov al, [Col] { 8 } stosb { 11 } end; Всего = 71 такт Как вы можете заметить, мы снизили число тактов со 153-х до 71-го... Весьма впечатляет. Текущий PutPixel ASPHYXIA занимает 48 тактов. Как вы можете видеть, прочесывая ваши процедуры несколько раз, вы можете убрать ненужные команды, что весьма ускорит скорость вашей программы. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Определение 3D объекта: Рисование объекта в 3D пространстве штука не простая. Сидение и вычерчивание набора X,Y,Z точек может быть трудоемким делом. Так, давайте взглянем на три оси в которых будем рисовать: Y Z /|\ / | / X<-----|-----> | \|/ X - горизонтальная ось, слева направо. Y - вертикальная, сверху вниз. Z - глубина, идущая прямо вглубь экрана. В этом уроке мы используем линии, что мы задаем двумя X,Y и Z координатами, по одной для каждого конца линии. Линия издалека из верхнего левого угла X и Y осей стремящаяся вниз, к правой части X и Y осей будет выглядеть так: { x1 y1 z1 x2 y2 z2 } ( (-10,10,-10),(10,-10,10) ) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Поворот точки при помощи матриц. Внимание: далее автор шутит по поводу того, как надо писать: МАТРИЦЫ или МАТРЕЦЫ :) (непереводимая игра амер. слов...) Иметь 3D объект бесполезно без возможности его некоторым образом повернуть. Для демонстрационных целей я начну работать в двух измерениях, X и Y. Давайте представим, что на графике есть две точка с координатами A и B. Y | /O1 (Cos (a)*A-Sin (a)*B , Sin (a)*A+Cos (a)*B) |/ (A,B) X<-----|------O--> | | Теперь, давайте повернем эту точку против часовой стрелки на 45 градусов: Новая точка с координатами A,B может быть легко посчитана с помощью Sin и Cos, применительно к нашему алгоритму круга, т.е.: A2:=Cos (45)*A - Sin (45)*B B2:=Sin (45)*A + Cos (45)*B Я вспомнил что это обычный 8 и 9 класс, в математике мы еще тяжелее с этим работали. Если есть проблемы, возьмите учебник за 8,9,10 класс, там все есть... В любом случае, теперь мы повернули объект по двум осям, вокруг оси Z. В виде матрицы решение выглядит так: [ Cos (a) -Sin (a) 0 0 ] [ x ] [ Sin (a) Cos (a) 0 0 ] . [ y ] [ 0 0 1 0 ] [ z ] [ 0 0 0 1 ] [ 1 ] Я не буду на этой стадии углубяться в математику матриц, так как по этому предмету есть много книг (Это не обучалка по матричной математики :) ). Чтобы перемножить матрицы нужно перемножить элементы ряда левой матрицы на элементы столбца правой и повторить это для всех столбцов левой матрицы. Я не объясняю этот процесс также хорошо, как мой лектор по математике на первом курсе, но смотрите как я получил A2 и B2. Вот другие матрицы: Матрица для вращения вокруг оси Y: [ Cos (a) 0 -Sin (a) 0 ] [ x ] [ 0 1 0 0 ] . [ y ] [ Sin (a) 0 Cos (a) 0 ] [ z ] [ 0 0 0 1 ] [ 1 ] Матрица для вращения вокруг оси X: [ 1 0 0 ] [ x ] [ 0 Cos (a) -Sin (a) 0 ] . [ y ] [ 0 Sin (a) Cos (a) 0 ] [ z ] [ 0 0 0 1 ] [ 1 ] Соединив все три матрицы вместе, мы может вывести 3D точки вокруг центра, находящегося в 0,0,0. Смотрите тестовую программу где показано как мы их совмещаем. В тестовой программе у нас есть постоянный, никогда не изменяющийся объект. Он вращается при помощи второй переменной, затем рисуется. Я уверен, многие из вас найдут что путь изменения базового объекта это круто. Один из способов - "пульсация" некоторой точки объекта в зависимости от ритма музыки играющей на заднем плане. Будьте изобретательны. Если прочувствуете, то сможете своих версии трансформеров наклепать. :) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Вывод 3D точки на экран. Иметь возмодность вращения 3D объетка бесполезно, если мы не можем его вывести на экран. Но как показать 3-ехмерную точку на 2-умерном экране ? Ответ требует некоторых объяснений. Посмотрите на следующую диаграмму: | ________------------- ____|___------ o Обект в X,Y,Z o1 Объект в X,Y,Z2 Глаз -> O)____|___ | ------________ | -------------- Поле видимости Экран Давайте определим, что центр экрана - горизонт нашего маленького 3D мира. Если мы нарисуем трехмерную линию от объекта "о" до центра глаза, и расположим пиксел на X и Y координатах, где он проходит через экран, мы заметим, что когда мы сделаем то же самое с объектом "о1", пиксел будет ближе к горизонту, даже несмотря на то, что их 3D X и Y координаты равны, но у "о1" Z больше чем у "о". Это значит, что чем дальше объект, тем ближе он к горизонту или тем он меньше. Звучит похоже, не так ли ? Но я слышу ваш плач: "как мы переведем это в формулу ???". Ответ прост. Проделим X и Y на Z. Подумайте об этом. Чем больше число на которое вы делите, тем ближе к нулю, или к горизонту - вот он, результат!. Это значит, что чем больше Z тем дальше объект! Вот форма вычисления: nx := 256*x div (z-Zoff)+Xoff ny := 256*y div (z-Zoff)+Yoff Заметьте: Zoff - это как далеко объект, Xoff - X значение, и Yoff - Y значение. В тестовой программе Xoff начинается на 160 и Yoff начинается на 100, так что объект в центре экрана. 256 на которые вы делите - это перспектива с которой вы смотрите. Изменение этой величины даст вам "эффект рыбьего глаза" при просмотре объекта. В любом случае у вас все есть! Нарисуйте пиксел в nx, ny и ВУАЛЯ! У вас есть 3D! Просто, не так ли ? Возможные улучшения. Эта программа не очень-то оптимизирована, она использует 12 умножений и 2 деления на точку. Asphyxia на данный момент имеет 9 умножений и 2 деления на точку. Вещественная математика применяется для всех вычислений в тестовой программе, а она медленная, так что должна применяться математика с фиксированной точкой (я дальше расскажу про нее). Процедура линии применяемая сейчас очень медленная. Надо применять Chain-4 для отсечения во время flipping. Должны быть добавлены: цветовые значения на линию, базовая модификация объекта, полигоны должны применяться вместо линий, должна выполняться работа с более чем объектом, clipping(обрезание) должен быть использован вместо "не рисования" и т.д. Короче, работы у вас много. В завершение. Книг по 3D много, как и программ... Так что изучайте их и сделаете свой уникальный 3D движок, в котором вы сможете сделать что захотите. Когда сделаете - мне пиво...