Привет всем! Рождество закончилось доели последний шоколад, так что пришло время заняться 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 - глубина, начинающаяся прямо с экрана. В этом уроке мы используем линии, что мы задаем 2 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. Смотрите тестовую программу где показано как мы их совмещаем. В тестовой программе у нас есть постоянный, никогда не изменяющийся объект. Он вращается по второй переменной, затем рисуетсяю Я уверен, многие из вас найдут пути изменения базового объекта во время движения. Один из способов - "пульсация" некоторой точки объекта в зависимости от ритма музыки играющей на заднем плане. Будьте изобретательны. Если прочувствуете, то сможете своих трансформеров наклепать. :) Having a rotated 3D object is useless unless we can draw it to screen. But how do we show a 3D point on a 2D screen? The answer needs a bit of explaining. Examine the following diagram : | ________------------- ____|___------ o Object at X,Y,Z o1 Object at X,Y,Z2 Eye -> O)____|___ | ------________ | -------------- Field of vision Screen Вывод 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. Доджны быть добавлены цветовые значения на линию, base object morphing could be put in, полигоны должны применяться вместо линий, работа с более чем объектом должна быть выполнена, clipping должен быть использован вместо "не рисования" и т.д. Короче, работы у вас много. В завершение. Книг по 3D много, как и программ... Так что изучайте их и сделаете свой уникальный 3D движок. Когда сделаете - мне пиво...