--==[ PARTE 8 ]==--
Para obter os programas mencionados neste tutorial, por favor baixe o zip com a versão antiga do tutorial, neste link.
[Nota: essas coisas entre colchetes foram adicionadas pelo Snownan. O texto original ficou quase todo inalterado, exceto pela inclusão do material em C++]
Introdução
Olá todo mundo! O Natal acabou, os últimos chocolates foram comidos, então é hora de continuar com isso, a oitava parte da série de treinamentos de demos ASPHYXIA. Essa parte, em particular, é primariamente sobre 3D, mas também tem algo sobre otimização.
Se você já é um guru em 3D, você pode bem pular esse arquivo: dê uma olhada no programa de amostra e volte a dormir, porque eu vou explicar em pequenos detalhes como as rotinas funcionam exatamente 😉
Se você gostaria de me contactar, ou ao time, há muitos modos que você pode fazê-lo:
1) Escrever uma mensagem para Grant Smith/Denthor/Asphyxia em email privado na ASPHYXIA BBS.
2) Escrever uma mensagem aqui na conferência de Programação, na
For Your Eyes Only BBS (do qual eu sou Moderador)
De preferência use isso se você tem uma pergunta geral
de programação ou problemas de que outros se beneficiariam.
4) Escrever para Denthor, Eze ou Livewire na Connectix.
5) Escrever para:
Grant Smith
P.O.Box 270 Kloof
3640
Natal
6) Ligar para mim (Grant Smith) no número (031) 73 2129
(deixe uma mensagem se você ligar quando eu estiver na faculdade)
7) Escreva para mcphail@beastie.cs.und.ac.za na InterNet, e
mencione a palavra Denthor perto do topo da carta.
OBS1 : Se você é um representante de uma companhia ou BBS e quer que a
ASPHYXIA faça um demo para você, mande um email pra mim; podemos
discutir.
OBS2 : Se você fez/tentou fazer um demo, MANDE PARA MIM!
Estamos nos sentindo muito solitários e queremos encontrar/ajudar/trocar
código com outros grupos de demos. O que você tem a perder? Mande uma
mensagem aqui e podemos ver como transferir. Nós realmente queremos
ouvir de você.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Otimização
Antes de começar com o 3D, eu gostaria de enfatizar que muitas dessas rotinas, e provavelmente a maioria das suas, poderiam ser aceleradas usando um pouco de otimização. Deve-se entender, porém, que você deve prestar atenção no QUE otimizar… converter uma rotina que só é chamada uma vez, numa rotina em assembly pode até mostrar seus méritos como programador, mas não faz absolutamente nada para acelerar seu programa. Algo que é chamado muito frequentemente é algo que precisa ser o mais rápido o possível. Para alguns, uma rotina muito usada é a PutPixel. Aqui está a putpixel que eu te dei semana passada:
[Nota: Snowman falando! Eu consultei a documentação oficial da Intel e notei que Denthor está se baseando em clocks do 8088 e não dos processadores 286, 386 e 486. Eu aproveitei e incluí a informação para os outros processadores.]
Procedure Putpixel (X,Y : Integer; Col : Byte; where:word); BEGIN -clock ticks- Asm 8088 286 386 486 push ds 14 3 2 3 push es 14 3 2 3 mov ax,[where] 8 5 4 1 mov es,ax 2 2 2 3 mov bx,[X] 8 5 4 1 mov dx,[Y] 8 5 4 1 push bx 15 3 2 1 mov bx, dx 2 2 2 1 mov dh, dl 2 2 2 1 xor dl, dl 3 2 2 1 shl bx, 1 2 2 3 3 shl bx, 1 2 2 3 3 shl bx, 1 2 2 3 3 shl bx, 1 2 2 3 3 shl bx, 1 2 2 3 3 shl bx, 1 2 2 3 3 add dx, bx 3 2 2 1 pop bx 12 5 4 4 add bx, dx 3 2 2 1 mov di, bx 2 2 2 3 xor al,al 3 2 2 1 mov ah, [Col] 8 5 4 1 mov es:[di],ah 10 3 2 1 pop es 12 5 7 3 pop ds 12 5 7 3 End; --------------- END; 153 75 76 52 Total ticks
NOTA: Não leve meus clocks como se fossem um evangelho, provavelmente tem um ou dois errados.
Certo, agora alguma otimização. Primeiramente, se você tem instruções de 286 ligadas, você pode colocar os 6 shl,1 com um shl,6. Em segundo, o compilador Pascal automaticamente faz o push e pop de ES, então essas duas linhas podem ser removidas. DS:[SI] não é alterado nesse procedimento, então podemos remover isso também. E mais, além de mover COL para AH, movemos para AL e chamamos stosb (es:[di]:=al; inc di). Vamos dar uma olhada na rotina agora:
Procedure Putpixel (X,Y : Integer; Col : Byte; where:word); BEGIN -clock ticks- Asm 8088 286 386 486 mov ax,[where] 8 5 4 1 mov es,ax 2 2 2 3 mov bx,[X] 8 5 4 1 mov dx,[Y] 8 5 4 1 push bx 15 3 2 1 mov bx, dx 2 2 2 1 mov dh, dl 2 2 2 1 xor dl, dl 3 2 2 1 shl bx, 6 8 11 3 2 add dx, bx 3 2 2 1 pop bx 12 5 4 4 add bx, dx 3 2 2 1 mov di, bx 2 2 2 3 mov al, [Col] 8 5 4 1 stosb 11 3 4 5 End; ---------------- END; 95 56 43 27 Total ticks
Agora, vamos mover o valor de BX diretamente para DI, com isso, removendo um push e pop caros. O MOV e o XOR de DX podem ser substituídos pelo seu equivalente SHL DX,8
[Nota: Se você está rodando num 286, esse último conjunto de otimizações faz a rotina ficas mais lenta, na verdade! Num 286, um SHL leva 5 clocks, mais o número de casas que você está deslocando. Por exemplo: um “SHL AX,6” levaria 11 batidas de clocks (5 para o SHL e 6 para o “6”).]
Procedure Putpixel (X,Y : Integer; Col : Byte; where:word); assembler; BEGIN -clock ticks- asm 8088 286 386 486 mov ax,[where] 8 5 4 1 mov es,ax 2 2 2 3 mov bx,[X] 8 5 4 1 mov dx,[Y] 8 5 4 1 mov di,bx 2 2 2 3 mov bx, dx 2 2 2 1 shl dx, 8 8 13 3 2 shl bx, 6 8 11 3 2 add dx, bx 3 2 2 1 add di, dx 3 2 2 1 mov al, [Col] 8 5 4 1 stosb 11 3 4 5 end; ---------------- 71 57 36 22 Total ticks
Como você pode ver, trouxemos os clocks de 153-52 (8088-486) para 71-22 (8088-486)… uma boa melhora. (A rotina putpixel atual do ASPHYXIA só tem 48 ticks). Como você pode ver, só de repassar nas suas rotinas algumas vezes, você pode encontrar e remover instruções desnecessárias, aumentando enormemente a velocidade do seu programa.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Definindo um objeto 3-D
Desenhar um objeto 3-D não é tão fácil. Sentar e plotar uma lista do pontos X, Y e Z pode ser algo que demanda muito tempo. Então, vamos primeiro dar uma olhada nos três eixos em que você está desenhando:
Y Z
/|\ /
| /
X<-----|----->
|
\|/
X é o eixo horizontal da esquerda pra direita. Y é o eixo vertical, de cima pra baixo. Z é a profundidade, indo direto pra dentro da tela.
Nesse treinamento, estamos usando linhas, então definimos 2 coordenadas X, Y e Z, um para cada final da reta. Uma linha de longe, na parte superior esquerda dos eixos X e Y, para mais perto no fundo à direita, dos eixos X e Y, poderia ser parecida com isso:
{ x1 y1 z1 x2 y2 z2 } ( (-10,10,-10),(10,-10,10) )
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Rodando um ponto com matrizes
Ter um objeto 3-D é inútil a menos que você o rode de algum modo. Para demonstração, vou começar trabalhando com duas dimensões, X e Y.
Digamos que você tem um ponto, A,B, num gráfico.
Y
| /O1 (Cos (a)*A-Sin (a)*B , Sin (a)*A+Cos (a)*B)
|/ (A,B)
X<-----|------O-->
|
|
Agora, digamos que queiramos rodar esse ponto em 45 graus, no sentido anti-horário. O novo A,B pode ser facilmente calculado usando seno e cosseno, através de uma adaptação do nosso algoritmo de círculo, ou seja
A2:=Cos (45)*A - Sin (45)*B
B2:=Sin (45)*A + Cos (45)*B
Eu me lembro que, na oitava série, a gente ia a fundo nesse tipo de matemática. Se você tiver problemas, procure uns bons livros de matemática e dê uma olhada; ele vai te ajudar com provas, etc.
De qualquer modo, temos agora um objeto rodado em duas dimensões, EM TORNO DO EIXO z. Em forma de matriz, a equação se parece com isso:
[Cos (a) -Sin (a) 0 0 ] [ x ] [Sin (a) Cos (a) 0 0 ] . [ y ] [ 0 0 1 0 ] [ z ] [ 0 0 0 1 ] [ 1 ]
Eu não vou entrar a fundo em matemáticas de matrizes nesse ponto, já que há muitos livros sobre esse assunto (isso não é parte da matemática de matrizes, no entanto). Para multiplicar uma matriz, adicionar os produtos da linha da matriz da esquerda por todas as colunas da matriz da direita, e repetir isso para todas as colunas da matriz da esquerda. Eu não explico isso tão bem quanto meu professor do primeiro ano, mas dê uma olhada em como eu derivei A2 e B2 acima. Aqui vão as outras matrizes:
Matriz para rodar em torno de 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 ]
Matriz para rodar em torno de X:
[ 1 0 0 ] [ x ] [ 0 Cos (a) -Sin (a) 0 ] . [ y ] [ 0 Sin (a) Cos (a) 0 ] [ z ] [ 0 0 0 1 ] [ 1 ]
Colocando essas matrizes juntas, podemos transladar nossos pontos 3D em torno de 0, 0, 0. Veja no programa de amostra como as colocamos juntas.
No programa de amostra, temos uma constante, nunca mudando o objeto de base. Este é rodado num segunda variável, que é então desenhada. Tenho certeza de que muitos de vocês podem pensar em modos maneiros de mudar o objeto base, os efeitos do que vai aparecer enquanto o objeto é rodado. Uma ideia é “pulsar” um certo ponto no objeto, de acordo com a batida da música sendo tocada no fundo. Seja criativo. Se você sentir vontade, pode fazer sua própria versão de Transformers 😉
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Desenhando um ponto 3D na tela.
Ter um objeto 3D rotacionado é inútil a menos que o desenhemos na tela. Mas como mostramos um ponto 3D numa tela 2D? A resposta precisa de um pouco de explicação. Examine o seguinte diagrama:
| ____-------- ___|___---- o Objeto em X,Y,Z o1 Olho -> O)___|___ Objeto at X,Y,Z2 | ----___ | -------- Tela Campo de visão
Vamos fingir que o centro da tela é o horizonte de nosso pequeno mundo 3D. Se desenharmos uma reta tridimensional do objeto “o” para o centro do nosso olho, e colocar um pixel nas coordenadas X e Y onde ela travessa a tela, notamos que quando fizermos o mesmo com o objeto “o1”, o pixel está mais perto do horizonte, embora suas coordenadas sejam as mesmas, mas o Z de “o1” seja maior que o de “o”. Isso significa que quanto mais longe o ponto está, mais perto do horizonte ele está, ou menor o objeto vai aparecer. Isso parece certo, não? Mas, ouço você gritando, como nós traduzimos isso numa fórmula? A resposta é muito simples. Divida seu X e seu Y pelo seu Z. Pense nisso. Quanto maior o número pelo qual você divide, mais perto de zero, ou o horizonte, é o resultado! Isso significa que, quanto maior Z, mas longe está o objeto! Aqui está uma forma em equação:
[Pascal] nx := 256x div (z-Zoff)+Xoff ny := 256y div (z-Zoff)+Yoff [C++] nx = ((256x) / (z-Zoff)) + Xoff; nx = ((256y) / (z-Zoff)) + Yoff;
NOTA: Zoff é o qual longe o objeto inteiro está, Xoff é o valor X do objeto, e Yoff é o valor Y do objeto. No programa de amostra, Xoff começa em 160 e Yoff em 100, de modo que o objeto esteja no meio da tela.
O 256 que você multiplica é a perspectiva com que você está vendo. Mudar isso te dá uma efeito de “visão de peixe”, quando você está visualizando o objeto. De qualquer modo, aí está! Desenhe um pixel em nx,ny e voila! Você está fazendo 3D! Fácil, não foi?
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Possíveis melhorias
Esse programa não é a rotina mais otimizada que você vai encontrar (;-))… ele usa 12 MUL’s e 2 DIV’s por ponto. (A rotina da Asphyxia atualmente tem 9 muls e 2 divs por ponto), matemática de números reais é usada para todos os cálculos no programa de amostra, o que é lento então a gente deveria implementar algo em matemática de pontos fixos (eu vou falar sobre isso mais pra frente). A rotina de linhas usada atualmente é lenta. Chain-4 poderia ser usada para cortar o tempo de flipping para a tela.
Valores de cores para as linhas deveriam ser adicionados, morphing do objeto poderia ser colocado, polígonos poderiam ser usados ao invés de linhas, poderia-se implementar a manipulação de mais de um objeto, clipping poderia ser implementado, ao invés de não desenhar nada se alguma parte dela está fora da tela.
Em outras palavras, você tem um monte de trabalho pela frente 😉
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Fechando
Há muitos livros por aí sobre 3D, e uns poucos exemplos bons também. Dê uma olhada neles, e use os melhores bits para criar a sua própria e única engine 3D, com a qual você pode fazer qualquer coisa que você quiser. Estou muito interessado em 3D (embora EzE e Goth tenham escrito a maioria das rotinas de 3D da ASPHYXIA), e gostaria de ver o que você pode fazer com ele. Deixe uma mensagem para mim de algum dos modos descritos acima.
Estou me aprofundando no obscuro mundo de mapeamento de texturas. Se alguém aí fora tiver algumas rotinas sobre esse assunto e estiver interessado em trocar comigo, me avisa!
O que fazer nos próximos tutoriais? Me ajude com essa! Há algum efeito/área que você gostaria de um pouco de informação? Mande-me uma mensagem!
Infelizmente não recebi nenhuma mensagem sobre as BBS’s que têm essa sériem então a lista que segue é a mesma da vez passada. Me deem seus nomes, sysops!
Aaaaargh!!! Tentei o que pude, mas não consigo pensar numa quote. Fica pra próxima, eu prometo! 😉
Tchau, por enquanto,
- Denthor
- Krull
Essas BBS’s possuem a série de treinamento de DEMOS da ASPHYXIA: (ordem alfabética)
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍËÍÍÍËÍÍÍÍËÍÍÍÍ» ºBBS Name ºTelephone No. ºOpenºMsgºFileºPastº ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÍÍÍÍÍÎÍÍÍÎÍÍÍÍÎÍÍÍ͹ ºASPHYXIA BBS #1 º(031) 765-5312 ºALL º * º * º * º ºASPHYXIA BBS #2 º(031) 765-6293 ºALL º * º * º * º ºConnectix BBS º(031) 266-9992 ºALL º * º º º ºFor Your Eyes Only BBSº(031)285-318ºA/Hº * º * º * º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÊÍÍÍÊÍÍÍÍÊÍÍÍͼ
Open = Open at all times or only A/H
Msg = Available in message base
File = Available in file base
Past = Previous Parts available