FILLED VECTORS (PARTE 1)


AVETE MAI SOGNATO DI SCRIVERE UNA ROUTINE 3D ALL'ALTEZZA DEI MIGLIORI DEMO ? CON UNA SERIE DI ARTICOLI IMPARERETE A FARE QUESTO E ALTRO...

Iniziamo ora con una serie di articoli mensili (2 o 3 al massimo), in linguaggio assembler per amiga, dedicati alla creazione di una routine 3D con le seguenti caratteristiche:

- DOUBLE BUFFERING
- ROTATION ON 3 AXIS
- ZOOM
- SORTING ( Z-VALUES & PRE-DETERMINED PRIORITY )
- LINES
- NORMAL SURFACES
- TRANSPARENT SURFACES!!!
- HIDDEN FACES
- DITHERING
- CLIPPING
SOLO IN CASO DI GRANDE RICHIESTA PUBBLICHERO' ARTICOLI RIGUARDANTI:
- TEXTURE MAPPING
- GOURAUD SHADING
- PHONG SHADING
- MOVING LIGHT SOURCE ON 3 AXIS

Inviare E-MAIL specificando la preferenza (anche in programmi completamente diversi , es.: ruotine di compressione e decompressione dati).


SPIEGAZIONE TERMINI

DOUBLE BUFFERING

Trattasi di un metodo per avere animazioni piu' fluide, sono necessari due o piu' blocchi di memoria: mentre un blocco viene visualizzato l'altro serve come buffer per calcolare il fotogramma successivo. A operazione compiuta i due blocchi di memoria vengono invertiti.

ROTATION ON 3 AXIS

la rotazione puo' essere su uno, due o tre assi, quali X, Y e Z. La ruotine che prenderemo in esempio ruota i punti su tutti tre gli assi, usando una tabella di seno e coseno pre-calcolata per velocizzare tutte le operazioni.

ZOOM

lo zoom serve per aumentare o diminuire le dimensioni dell'oggetto vettoriale, come se questo si avvicinasse o si allontanasse da noi.

SORTING

uno dei problemi maggiori e' sicuramente quello relativo alla ruotine di sorting. In pratica serve per dare una priorita' di disegno alle facce che compongono il nostro l'oggetto. Tale priorita' serve solo in alcuna casi, vediamo quali.

Facciamo un esempio pratico: un cubo tridimensionale sara' composto da 6 facce (nel nostro esempio in numero di punti e' trascurabile); in qualunque modo questo sia ruotato o zummato avra' un numero di facce massime visibili pari a 3. Le restanti 3 facce non sono visibili dall'osservatore (HIDDEN FACES!), quindi e' perfettamente inutile disegnarle. Mentre le cose sono semplici per queste ultime , tutto si complica per le facce visibili.

In questo esempio le facce visibili non si sovraporranno mai, quindi e' inutile dar loro una priorita' di disegno (non e' rilevante l'ordine con cui queste vengano visualizzate).

Facciamo un secondo esempio: un carro armato con tanto di cingoli!

In questo caso e' necessario dare una priorita' di disegno alle facce, onde evitare (in vista laterale) che i cingoli vengano disegnati prima del nucleo principale del carro armato, serebbe impensabile avere entrambi i cingoli sullo stesso lato: avete presente una macchina con tutte le 4 ruote a destra ???

In questo ultimo caso in sorting in base ai valori della Z e' necessario in modo da bilanciare il disegno, facendo disegnare prima un cingolo, poi il nucleo principale con il cannone e per ultimo l'altro cingolo. In altre parole una volta ruotato e zummato l'oggetto in questione, si prelevano solo le coordinate dalla Z e si effettua la media di ogni faccia, una volta calcolate tutte le medie si effettua un riordino delle facce in base al valore della Z: si disegna partendo dal fondo dell'oggetto e disegnando per ultime le facce piu' vicino a noi.

LINES

per abbellire il tutto e' possibile disegnare semplici linee e mescolarle con tutti gli altri metodi di disegni inclusi. NORMAL SURFACES

si tratta di semplici facce piane fillate e sovrapposte a quelle sottostanti. TRANSPARENT SURFACES

le facce trasparenti devono essere calcolate ma non visualizzate (non per nulla sono trasparenti). Deve essere possibile vederci attravarso: in questo caso devono essere visualizzate le facce interne dell'oggetto calcolato (anche se normalmente nascoste). Tornando sul cubo: 3 facce visibili e 3 no; nel caso una delle facce sia trasparente sara' necessario visualizzare anche le facce nascoste.

HIDDEN FACES

tramite un semplice algoritmo e' possibile capire se una faccia e' visibile oppure no (in questo caso non la visualizzeremo neppure per avere una maggiore velocita' di eseguzione del programma).

DITHERING

questo procedimento serve per aggiungere una maschera grigliata alle facce solide, onde avere colori intermedi e effetti ombreggiatura.

CLIPPING

viene usato quando una faccia esce dalla zona visibile dello screen. Con questa routine e' possibile disegnare solo i pezzi di facce interne allo screen ed aliminare le parti esterne.

TEXTURE MAPPING

Tecnica per mappare un disegno in bitmap ai poligoni tridimensionali. Molto usati in giochi stile DOOM o simili, il disegno oltre ad essere ruotato sugli assi deve anche essere scalato.

GOURAUD SHADING

Metodo matematico per determinare il colore di una faccia in base ai gradi di ruotazione sia della faccia sia di un punto luminoso. La luce e' supposta costante all'interno del poligono.

PHONG SHADING

Al contrario del GOURAUD il colore della faccia deve essere calcolato punto per punto, attenuando la sfacettatura delle immagini. Dal punto di vista estetico il PHONG e' molto migliore del GOURAUD ma richiede molti piu' calcoli e computer piu' potenti.

MOVING LIGHT SOURCE ON 3 AXIS

Sia con GOURAUD e PHONG SHADING e' necessario avere le coordinate (x,y,z) di un punto luminoso in modo da calcolare i vari colori di illuminazione , muovendo questo punto luminoso deve cambiare tutta l'illuminazione dell'oggetto.


SPIEGAZIONE DETTAGLIATA

In questo articolo analizzeremo solo i primi 3 punti, gli altri verranno spiegati nei mesi seguenti.

DOUBLE BUFFERING

Nell'esempio ho utilizzato ben 6 allocazioni di memoria, in modo da far un effetto simile al Motion Blur (premere il tasto destro del mouse).

In pratica su uno di questi (calcolascreen) effettuo il calcolo del fotogramma successivo mentre gli altri li visualizza in sequenza, avremo quindi:

screen1 = ultimo fotogramma calcolato

screen2 = 1 fotogramma prima dell'ultimo calcolato

screen3 = 2 fotogrammi prima dell'ultimo calcolato

screen4 = 3 fotogrammi prima dell'ultimo calcolato

screen5 = 4 fotogrammi prima dell'ultimo calcolato

Una volta chiarito l'ordine di visualizzazione del disegno utilizzo il blitter per cancellare il contenuto del bitplane (CalcolaScreen).

SwapScreen:

** Cambiamento puntatori allo screen per il fotogramma successivo

	move.l	CalcolaScreen,d0
	move.l	Screen1,CalcolaScreen
	move.l	Screen2,Screen1
	move.l	Screen3,Screen2
	move.l	Screen4,Screen3
	move.l	Screen5,Screen4
	move.l	d0,Screen5

** visualizzazione tramite copper dei puntatori allo screen

	lea	planeaddress,a0
	move.l	Screen5,d0
	move.w	d0,6(a0)
	swap	d0
	move.w	d0,2(a0)
	swap	d0
	move.l	Screen4,d0
	move.w	d0,14(a0)
	swap	d0
	move.w	d0,10(a0)
	swap	d0
	move.l	Screen3,d0
	move.w	d0,22(a0)
	swap	d0
	move.w	d0,18(a0)
	swap	d0
	move.l	Screen2,d0
	move.w	d0,30(a0)
	swap	d0
	move.w	d0,26(a0)
	move.l	Screen1,d0
	move.w	d0,38(a0)
	swap	d0
	move.w	d0,34(a0)

** utilizzo del blitter per cancelare CalcolaScreen

	lea	$dff000,a6
	move.w	#$8400,$96(a6)
WaitBlitter:
	btst	#6,2(a6)
	bne.s	WaitBlitter
	move.w	#$0400,$96(a6)
	move.l	#$01000000,$40(a6)
	moveq	#-1,d0
	move.l	d0,$44(a6)
	move.l	CalcolaScreen,$54(a6)
	move.w	#0,$66(a6)
	move.w	#$4014,$58(a6)
	rts

ROTATION ON 3 AXIS

Questa routine preleva le coordinate X,Y e Z dalla variabile WPoints e li ruota su tutti gli assi. I gradi di ruotazione sull'asse X li preleva da Xangle, Y da Yangle e Z da Zangle.

La routine SinCos preleva (da SinTab) i valori (precalcolati) del seno e del coseno dell'angolo di ruotazione e li salva in variabili quali Xsin,Xcos,Ysin, Ycos,Zsin e Zcos.

**********************************
** Ruota attorno tutti gli assi **
**********************************
Rotate:
	move.w	#$fff,d4	; $fff = numero massimo di valori in SINTAB

	move.w	Zangle,d0	; angolo ruotazione Z
	and.w	d4,d0		; and con $fff
	move.w	d0,Zangle
	jsr	SinCos		; prelevamento Seno e Coseno
	move.w	d1,Zsin		; seno di Z
	move.w	d2,Zcos		; coseno di Z

	move.w	Yangle,d0	; vedi sopra mo con Yangle
	and.w	d4,d0
	move.w	d0,Yangle
	jsr	SinCos
	move.w	d1,Ysin
	move.w	d2,Ycos

	move.w	Xangle,d0	; vedi sopra ma con Xangle
	and.w	d4,d0
	move.w	d0,Xangle
	jsr	SinCos
	move.w	d1,Xsin
	move.w	d2,Xcos

	lea	Wpoints,a0	; coordinate X,Y,Z originali
	lea	IntX,a3		; salvataggio coordinate X (calcolate)
	lea	IntY,a4		; salvataggio coordinate Y (calcolate)
	lea	IntZ,a5		; salvataggio coordinate Z (calcolate)
	move.w	#npoints-1,d0	; numero di punti -1 (quindi 1858-1)
Zrotate:
	move.w	Zsin(pc),d1	; sin Z
	move.w	Zcos(pc),d2	; cos Z
	move.w	(a0)+,d6	; X
	move.w	(a0)+,d7	; Y
	muls	d6,d2		; X * cos Z
	muls	d7,d1		; Y * sin Z
	sub.l	d1,d2		; (X * cos Z)-(Y * sin Z)
	add.l	d2,d2		; ((X * cos Z)-(Y * sin Z)) * 2
	swap	d2		; (((X * cos Z)-(Y * sin Z)) * 2) / 32768
	move.w	d2,d5		; NEW X !!!
	move.w	Zsin(pc),d1	; sin Z
	move.w	Zcos(pc),d2	; cos Z
	muls	d6,d1		; X * sin Z
	muls	d7,d2		; Y * cos Z
	add.l	d1,d2		; (Y * cos Z) + (X * sin Z)
	add.l	d2,d2		; ((Y * cos Z) + (X * sin Z)) * 2
	swap	d2		; (((Y * cos Z) + (X * sin Z)) * 2) / 32768
	move.w	d2,d6		; NEW Y
Yrotate:
	move.w	Ysin(pc),d1
	move.w	Ycos(pc),d2
	move.w	(a0)+,d3	; Z
	muls	d3,d2
	muls	d5,d1
	sub.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,d7		; NEW Z
	move.w	Ysin(pc),d1
	move.w	Ycos(pc),d2
	muls	d3,d1
	muls	d5,d2
	add.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,(a3)+	; FINAL X
Xrotate:
	move.w	Xsin(pc),d1
	move.w	Xcos(pc),d2
	muls	d6,d2
	muls	d7,d1
	sub.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,(a4)+	; FINAL Y
	move.w	Xsin(pc),d1
	move.w	Xcos(pc),d2
	muls	d6,d1
	muls	d7,d2
	add.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,(a5)+	; FINAL Z
	dbf	d0,Zrotate
	rts
***************************
** Calcola seno e coseno **
***************************
Sincos:
	lea	SinTab,a1	; seno e coseno precalcolato
	move.w	d0,d2
	add.w	d0,d0
	move.w	(a1,d0.w),d1	; New SIN
	move.w	#$c00,d3	; 4/3 of 360°
	cmp.w	d3,d2
	blt.s	Plus9
	sub.w	d3,d2
	add.w	d2,d2
	move.w	(a1,d2.w),d2	; New COS
	rts
Plus9:	add.w	#$400,d2		; 1/4 di 360 gradi
	add.w	d2,d2
	move.w	(a1,d2.w),d2	; New COS
	rts

ZOOM

La ruotine di Zoom serve per aumentare o diminuire le demensioni degli oggetti in modo da aggiungere l'effetto avvicinamento o allontanamento.

A questo proposito sono necessarie 3 coordinate per ogni punto (X,Y,Z) e da queste ne verranno ricavate solo 2 (X,Y).

Una semplificazione dei calcoli 3D che gestiscono lo zoom potrebbero essere:


X final = ( X * zoom ) / ( 1000 + Z )
Y final = ( Y * zoom ) / ( 1000 + Z )

dove:

X , Y , Z = sono le coordinate di un punto

zoom = valore di zoom intermedio tra 0 e 1000

1000 = valore massimo dello zoom

Zoom:
	lea	IntX,a1			; coordinata X
	lea	IntY,a2			; coordinata Y
	lea	IntZ,a3			; coordinata Z

	move.l	CalcolaScreen,a4	; screen address
	lea	Tabella,a5		; valore Y moltiplicati per 40

	moveq	#0,d0
	move.w	#npoints-1,d0
	move.w	Dist(pc),d3		; valore zoom
	move.w	#1000,d4		; volore massimo zoom

WaitBlit:
	btst	#6,$dff002
	bne.s	WaitBlit
CalcAll:
	move.w	(a1)+,d6		; X
	muls	d3,d6			; X * zoom
	move.w	(a3)+,d2		; Z
	move.w	d2,d7			; d7 = d2 = Z
	add.w	d4,d2			; 1000 + Z
	divs.w	d2,d6			; NEW X
	add.w	Xorg(pc),d6		; X = X + orgX

	move.w	(a2)+,d1		; Y
	neg.w	d1			; d1 = neg d1
	muls	d3,d1			; Y * zoom
	add.w	d4,d7			; 1000 + Z
	divs.w	d7,d1			; NEW Y
	add.w	YOrg(pc),d1		; Y = Y + orgY

; X = d6
; Y = d1
	move.w	d6,d5			; d6 = d5 = X
	lsr.w	#3,d5			; d5 = d5 / 8
	add.w	d1,d1			; d1 = d1 * 2
	add.w	(a5,d1.w),d5		; d5 = d5 * 40 (byte for row)
	not.b	d6			; d6 = not d6

	bset	d6,(a4,d5.w)		; set pixel

	dbf	d0,CalcAll
	rts

Nell'archivio sono incluse due demo:

3D-points : esempio e sorgente di questo articolo

3D-demo : demo della routine 3D (presto avrete il sorgente)...

Le routine somo molto vecchie (1990) e testate solo su Amiga 500 e 2000 , pertanto non ottimizate per microprocessori piu' veloci.

La mia ultima routine 3D (68020+) riesce a gestire il satellite (180 punti e 170 facce) a 25 Frames/second in modo 256 colori, ma essendo stata creata per un gioco (quindi commerciale), non ne pubblichero' il sorgente.

SORRY....


Pagina Principale


    Scritto da: Alfredo Ornaghi      e-mail: ted@intercom.it
                ITALY                   tel: