• No results found

Algoritmen f¨or tr¨adgenereringen funkar bra och det g˚ar f˚ar till en m¨angd olika tr¨adarter. M˚anga saker beskrivna i originalrapporten hann tyv¨arr inte implementeras. L¨oven ¨ar t¨ankt att vara riktig geometri med en m¨angd polygoner men implementerades h¨ar enbart som en enkel quad med en textur. Detta g¨or att orienteringen av l¨oven ser ganska konstigt ut d˚a det ¨ar sv˚art att f˚a placeringen p˚a l¨oven att hamna s˚a det ser ut som l¨ovet verkligen v¨axer p˚a kvisten.

Min implementation har vissa artefakter som jag inte blivit klok p˚a. D¨aribland att l¨angden p˚a grenarna verkar inte st¨amma riktigt vilket p˚averkar m¨angden l¨ov och fortsatta niv˚aer av grenar. ¨Aven slumptermen som adderas verkar st¨alla till problem f¨or vissa random seeds. Om detta ¨ar en artefakt som jag orsakat eller originalrapporten f¨orblir oklart.

M¨angden tr¨adarter som jag testat har varit ganska begr¨ansat d˚a enbart ett par stycken f¨ardiga tr¨adarter fanns tillg¨angliga. Fler implementationer finns med en m¨angd olika arter men konvertering mellan mitt filformat och deras hade d˚a varit n¨odv¨andig vilket p˚a grund av tidsbrist aldrig blev av.

5.2. Multitr˚adning

N¨ar geometrin skapas s˚a kopieras detta till en separat buffert som sedan kopieras in till en VBO. Detta skulle kunna f¨orb¨attras genom att mappa minnet mellan VBO:n och klientapplikationen och kopiera rakt in i VBO:n fr˚an uppdateringstr˚aden. Detta ¨ar d¨aremot sv˚art att genomf¨ora d˚a en renderingskontext g˚ar ej vanligen att dela mellan olika tr˚adar. Idealt hade man velat att uppdateringstr˚aden mappar bufferten till grafikkortet utan mellanh¨ander.

Det finns m¨ojlighet att dela renderingskontext mellan olika tr˚adar men detta ¨ar inget som finns tillg¨angligt via SDL. F¨or att byta renderingskontext skulle man f˚a anropa glXMakeCurrent och liknande plattformsberoende funktioner. En annan m¨ojlighet vore f¨or renderingstr˚aden att mappa bufferten och l˚ata uppdateringstr˚aden streama direkt till VBO:n. Detta ¨ar t¨ankbart men problem som hur renderingstr˚aden vet n¨ar den ska kunna unmappa bufferten igen dyker upp. Under tiden bufferten ¨ar mappad s˚a kan inte grafikkortet utf¨ora n˚agot arbete.

Den enklaste l¨osningen valdes ist¨allet d¨ar uppdateringstr˚aden allokerar en buffert och kopierar ¨over geometrin. Pekaren till denna buffert delas med renderingstr˚aden som streamar ¨over geometrin till grafikkortet. N¨asta uppdatering allokeras en ny buffert och

den gamla avallokeras. Detta skulle kunna optimeras rej¨alt eftersom antalet vertexar ¨ar konstant fr˚an frame till frame, men d˚a har man inte l¨angre m¨ojlighet att kunna variera m¨angden vertexar p˚a ett flexibelt vis i syfte att snabba upp uppdateringen/renderingen. I 2.2.4 n¨amns interlock-operationer men detta ¨ar inget jag kan se n˚agon anv¨andning av d˚a tekniken anv¨ands fr¨amst till cirkul¨ara bufferts d¨ar en tr˚ad fyller i ena ¨anden och en annan t¨ommer i andra. Kanske f¨or att visualisera en “animering” av ett tr¨ads uppbyggnad kunde vara relevant i detta avseende. Samtidigt n¨amns i 3.2 att detta ej finns tillg¨angligt i userspace linux vilket varf¨or jag ej kunnat anv¨anda mig av detta.

5.3. Vindp˚averkan

Enligt [5] kr¨avs att tr¨aden genereras om med samma random seed f¨or att uppdatera positionerna efter en viss tid med vind aktiverat. Om man analyserar lite vad som egentligen ¨andras s˚a r¨acker det att uppdatera orienteringen i en viss ordning. Under genereringspasset sparar jag undan de stammar och l¨ov som ett visst segment har skapat och kan p˚a s˚a vis rekursivt traversera igenom hela tr¨adet i samma ordning som de skapades. Ingen omber¨akning av radier, l¨angder eller annat ¨ar n¨odv¨andigt. Detta b¨or vara en relativt bra optimering men inga profileringar har utf¨orts f¨or att bevisa detta.

5.4. Level of Detail

Ett metod vore att helt enkelt generera om tr¨aden med ett f¨arre antal polygoner beroende p˚a avst˚and fr˚an objektet till betraktaren. Detta motsvarar tekniker som Discrete LOD (DLOD) men skillnaden ¨ar att man genererar LOD-niv˚aer dynamiskt ist¨allet f¨or att spara det som f¨orgjorda modeller. Tr¨aden genereras med samma random seed.

Detta diskuteras ¨aven i [5] med argument som att genereringen tar mellan 1 - 10 sekunder f¨or ett enstaka tr¨ad och s˚aledes oacceptabelt. Ist¨allet valde de att beh˚alla den geometriska datan intakt och omtolka datan p˚a olika vis. Avst˚and som varierar fr˚an medel till l˚anga kan man utan problem rendera tr¨aden som enbart en tr¨adstam och l¨ov utan att det blir n˚agon st¨orre m¨arkbar skillnad. Denna l¨osning har ett problem; de f¨oruts¨atter att renderingen g¨ors med immediate mode rendering d¨ar man l¨att kan hoppa ¨

over enstaka niv˚aer av grenar. De n¨amner heller ingenting om krav p˚a realtid.

Min metod med att generera om tr¨aden visar sig inte vara n˚agot problem med da- gens processorer. En generering av ett h¨oguppl¨ost tr¨ad g¨ors bara p˚a n˚agon br˚akdel av en sekund och genom att ¨aven kombinera detta med multitr˚adning kan man undvika ryckigheter under tiden tr¨aden genereras. Tyv¨arr l¨ampar sig inte metoden n¨ar kloner av tr¨ad ¨ar inblandade d˚a alla kloner anv¨ander sig av samma geometri.

5.5. Instancing

I skrivande stund har enbart Direct3D 9 st¨od f¨or s˚a kallad Hardware Instancing d¨ar alla instanser kan ritas via ett enda ritanrop och sedan anger man hur var och en ska transformeras individuellt. Detta s¨ags dock inte vara n˚agot som beh¨ovs med OpenGL

d˚a ett ritanrop anses var mindre kostsamt ¨an motsvarande i Direct3D. En extension f¨or OpenGL finns tydligen utvecklad av NVIDIA som heter NVX instanced arrays1och som m¨ojlig¨or ¨akta hardware instancing. Vill man uppn˚a ett liknande resultat i OpenGL utan att f¨orlita sig p˚a h˚ardvaruspecifika extensions s˚a finns en teknik kallad pseudo-instancing

2. Ingen av metoderna har testats i arbetet och huruvida dessa p˚averkar prestandan ¨ar

ok¨ant.

1http://developer.nvidia.com/object/opengl-nvidia-extensions-gdc-2006.html 2

http://developer.download.nvidia.com/SDK/9.5/Samples/DEMOS/OpenGL/src/ glsl pseudo instancing/docs/glsl pseudo instancing.pdf

6. Slutsatser

Detta examensarbete har resulterat i en applikation som uppfyllde de uppsatta kraven med att generera tr¨ad helt procedurellt vid uppstart och rendera tr¨aden med p˚averkan av vind i realtid med hj¨alp av multitr˚adade tekniker.

Metoden med att uppdatera geometri i en skild tr˚ad och rendering i en annan f¨orbeh˚aller sig ganska naturlig d˚a det kan ske n¨astintill helt parallellt utan n˚agra st¨orre synkronise- ringsproblem. N˚agra av f¨ordelarna med att implementera det hela p˚a CPU:n kan vara att man avlastar grafikkortet och kan p˚a s˚a vis ¨agna stora delar p˚a grafiska effekter. D¨aremot ¨ar det inte sagt att detta ¨ar en optimal metod och ett b¨attre resultat hade f¨ormodligen kunnat ˚astadkommas genom att ber¨akna all uppdatering av geometri p˚a grafikkortet.

Av de fr˚agor st¨allda om en multitr˚adad l¨osning presterar b¨attre en den enkeltr˚adade varianten s˚a ¨ar svaret definitivt ja, men f¨or att uppn˚a bra prestanda i spel s˚a ¨ar en GPU-baserad l¨osning att f¨oredra.

Litteraturf¨orteckning

[1] Akenine-M¨oller, Tomas, Eric Haines. “Real-Time Rendering”, Second Edition. Wel- lesley, MA, USA. A K Peters, Ltd., 2002.

[2] Deitel, Harvey M., Paul J. Deitel, David R. Choffnes. “Operating Systems”, Third Edition. Prentice Hall, 2003.

[3] Floyd, R.W., L. Steinberg, “An adaptive algorithm for spatial grey scale”. Procee- dings of the Society of Information Display 17, 75-77, 1976.

[4] Shreiner, Dave, Mason Woo, Jackie Neider, Tom Davis. “OpenGL(R) Program- ming Guide: The Official Guide to Learning OpenGL(R), Version 2”, Fifth Edition. Addison-Wesley Professional, 2005.

[5] Weber, Jason, Joseph Penn. “Creation and Rendering of Realistic Trees”, Computer Graphics (Proc. Siggraph 95), ACM Press, New York, 1995.

A. Sk¨armdumpar

B. Exempelkod

B.1. Uppdatering

Listing B.1: tree.cpp

void Tree : : update ( ) {

params−>r e s e t s e e d ( ) ; t i m e = t i m e r −>s e c o n d s ( ) ;

/∗ u p d a t e t r u n k stem and a l l i t s sub−s t e m s r e c u r s i v e l y ∗/

T r a n s f o r m a t i o n t r f = s e g m e n t b e y o n d f l a r i n g −>t r a n s f o r m a t i o n ( ) ; t r u n k −>update ( t r f ) ; /∗ u p d a t e c o m p l e t e , c o n s t r u c t mesh ∗/ f i l l b u f f e r s ( ) ; } void Tree : : f i l l b u f f e r s ( ) { Mesh b r a n c h e s ; { Mesh & tmp = b r a n c h e s ; /∗ p o i n t e r o f f s e t s ∗/ tmp . n p o i n t s = t o t a l p o i n t s ; tmp . v s i z e = s i z e o f ( math : : V e c 3 t ) ∗tmp . n p o i n t s ; tmp . t s i z e = s i z e o f ( math : : V e c 2 t ) ∗tmp . n p o i n t s ; tmp . i s i z e = s i z e o f ( unsigned i n t ) ∗tmp . n p o i n t s ∗ 6 ; tmp . n s i z e = s i z e o f ( math : : V e c 3 t ) ∗tmp . n p o i n t s ; /∗ a l l o c a t e b u f f e r ∗/ tmp . b u f f e r = ( unsigned char ∗ ) m a l l o c ( tmp . v s i z e + tmp . t s i z e + tmp . n s i z e + tmp . i s i z e ) ; tmp . v b u f = ( math : : V e c 3 t ∗ ) tmp . b u f f e r ; tmp . t b u f = ( math : : V e c 2 t ∗ ) ( tmp . b u f f e r+tmp . v s i z e ) ;

tmp . nbuf = ( math : : V e c 3 t ∗ ) ( tmp . b u f f e r+tmp . v s i z e+tmp . t s i z e ) ; tmp . i b u f = ( unsigned i n t ∗ ) ( tmp . b u f f e r+tmp . v s i z e+ tmp . t s i z e+tmp . n s i z e ) ; math : : V e c 3 t ∗ v b u f = tmp . v b u f ; unsigned i n t ∗ i b u f = tmp . i b u f ; unsigned i n t o f f s e t = 0 ; math : : V e c 2 t ∗ t b u f = tmp . t b u f ;

math : : V e c 3 t ∗ nbuf = tmp . nbuf ;

/∗ f i l l b u f f e r s r e c u r s i v e l y ∗/

t r u n k −> f i l l b r a n c h b u f f e r s (&vbuf , &i b u f , &o f f s e t , &t b u f , &nbuf ) ; a s s e r t ( v b u f <= tmp . v b u f+tmp . v s i z e ) ; a s s e r t ( t b u f <= tmp . t b u f+tmp . t s i z e ) ; a s s e r t ( nbuf <= tmp . nbuf+tmp . n s i z e ) ; a s s e r t ( i b u f <= tmp . i b u f+tmp . i s i z e ) ; } Mesh l e a v e s ; { Mesh & tmp = l e a v e s ; /∗ p o i n t e r o f f s e t s ∗/ tmp . n p o i n t s = t o t a l l e a v e s ; tmp . v s i z e = s i z e o f ( math : : V e c 3 t ) ∗4∗ t o t a l l e a v e s ; tmp . t s i z e = s i z e o f ( math : : V e c 2 t ) ∗4∗ t o t a l l e a v e s ; tmp . n s i z e = s i z e o f ( math : : V e c 3 t ) ∗4∗ t o t a l l e a v e s ; /∗ a l l o c a t e b u f f e r ∗/ tmp . b u f f e r = ( unsigned char ∗ ) m a l l o c ( tmp . v s i z e + tmp . t s i z e + tmp . n s i z e ) ; tmp . v b u f = ( math : : V e c 3 t ∗ ) tmp . b u f f e r ; tmp . t b u f = ( math : : V e c 2 t ∗ ) ( tmp . b u f f e r + tmp . v s i z e ) ; tmp . nbuf = ( math : : V e c 3 t ∗ ) ( tmp . b u f f e r + tmp . v s i z e + tmp . t s i z e ) ; math : : V e c 3 t ∗ v b u f = tmp . v b u f ; unsigned i n t ∗ i b u f = tmp . i b u f ; unsigned i n t o f f s e t = 0 ; math : : V e c 2 t ∗ t b u f = tmp . t b u f ; math : : V e c 3 t ∗ nbuf = tmp . nbuf ;

/∗ f i l l b u f f e r s r e c u r s i v e l y ∗/

t r u n k −> f i l l l e a f b u f f e r s (&vbuf , &i b u f , &o f f s e t , &t b u f , &nbuf ) ; a s s e r t ( v b u f <= tmp . v b u f+tmp . v s i z e ) ; a s s e r t ( t b u f <= tmp . t b u f+tmp . t s i z e ) ; a s s e r t ( nbuf <= tmp . nbuf+tmp . n s i z e ) ; a s s e r t ( i b u f <= tmp . i b u f+tmp . i s i z e ) ; } /∗ copy t o s h a r e d memory ( c r i t i c a l s e c t i o n ) ∗/ { /∗ b r a n c h e s ∗/ { b o o s t : : t r y m u t e x : : s c o p e d l o c k L1 ( b r a n c h e s . mutex ) ; f r e e ( b r a n c h e s . b u f f e r ) ; b r a n c h e s = b r a n c h e s ; }

/∗ l e a v e s ∗/ { b o o s t : : t r y m u t e x : : s c o p e d l o c k L2 ( l e a v e s . mutex ) ; f r e e ( l e a v e s . b u f f e r ) ; l e a v e s = l e a v e s ; } } } Listing B.2: stem.cpp

void Stem : : update ( const T r a n s f o r m a t i o n & a t r f ) { const i n t n c u r v e r e s = p a r −>nCurveRes [ l e v e l ] ; t r f = a t r f ; S e g m e n t p t r l a s t s e g m e n t = u p d a t e l a s t s e g m e n t ; S e g m e n t i t e r a t o r n e x t s e g m e n t = u p d a t e n e x t s e g m e n t ; l a s t s e g m e n t −>make ( t r f , l a s t s e g m e n t −> r a d i u s ) ; f o r ( i n t i =1; i <n c u r v e r e s ; ++i ) { T r a n s f o r m a t i o n t r f = s t e m d i r e c t i o n ( l a s t s e g m e n t −> t r f , i ) ; S e g m e n t p t r ps = ∗ n e x t s e g m e n t ++;

wind sway (& t r f , ps−> r a d i u s , Re al ( i ) / Re al ( n c u r v e r e s −1) , t r e e −> t i m e ) ; ps−>update ( t r f , ps−> r a d i u s ) ; typedef s t d : : v e c t o r <Stem ∗ > : : i t e r a t o r S t e m i t e r ; f o r ( S t e m i t e r j=ps−> s p a w n e d s u b s t e m s . b e g i n ( ) ; j != ps−> s p a w n e d s u b s t e m s . end ( ) ; ++j ) { Stem ∗ stem = ∗ j ; V e c 3 t s u b s t e m o r i g i n = t r f . g l o b a l o r i g i n + t r f . o r i e n t a t i o n ∗ V e c 3 t ( 0 , stem−> o f f s e t , 0 ) ; stem−>update ( T r a n s f o r m a t i o n ( t r f . o r i e n t a t i o n ∗ stem−> q u a t , math : : VEC3 ZERO, s u b s t e m o r i g i n ) ) ;

} typedef s t d : : v e c t o r <L e a f ∗ > : : i t e r a t o r L e a f i t e r ; f o r ( L e a f i t e r k=ps−> s p a w n e d l e a v e s . b e g i n ( ) ; k!= ps−> s p a w n e d l e a v e s . end ( ) ; ++k ) { L e a f ∗ l e a f = ∗k ; V e c 3 t l e a f o r i g i n = t r f . g l o b a l o r i g i n + t r f . o r i e n t a t i o n ∗ V e c 3 t ( 0 , l e a f −> o f f s e t , 0 ) ;

l e a f −>make ( T r a n s f o r m a t i o n ( l e a f −> q u a t ∗ t r f . o r i e n t a t i o n , math : : VEC3 ZERO, l e a f o r i g i n ) ) ;

}

l a s t s e g m e n t = ps ; }

}

B.2. Rendering

Listing B.3: mesh technique.cpp

void M e s h t e c h n i q u e : : d r a w b r a n c h e s ( const t r e e : : Mesh & b r a n c h e s ) { { l o c k t y p e L ( b r a n c h e s . mutex ) ; i f ( L . l o c k e d ( ) ) s t r e a m c o p y t o v b o ( b r a n c h e s ) ; } g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ; g l V e r t e x P o i n t e r ( 3 , GL FLOAT, 0 , 0 ) ; g l E n a b l e C l i e n t S t a t e (GL VERTEX ARRAY) ; g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ; g l T e x C o o r d P o i n t e r ( 2 , GL FLOAT, 0 , ( char ∗ ) b r a n c h e s p a s s . v s i z e ) ; g l E n a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ;

g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ;

g l N o r m a l P o i n t e r (GL FLOAT, 0 , ( char ∗ ) b r a n c h e s p a s s . v s i z e+ b r a n c h e s p a s s . t s i z e ) ;

g l E n a b l e C l i e n t S t a t e (GL NORMAL ARRAY) ;

g l B i n d B u f f e r (GL ELEMENT ARRAY BUFFER, b r a n c h e s v b o [ 1 ] ) ; g l E n a b l e C l i e n t S t a t e (GL INDEX ARRAY) ;

glDrawElements (GL TRIANGLES, b r a n c h e s p a s s . n p o i n t s ∗ 6 , GL UNSIGNED INT , 0 ) ;

g l D i s a b l e C l i e n t S t a t e (GL VERTEX ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL INDEX ARRAY) ;

g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL NORMAL ARRAY) ;

}

void M e s h t e c h n i q u e : : s t r e a m c o p y t o v b o ( const t r e e : : Mesh & b r a n c h e s ) {

b r a n c h e s p a s s . n p o i n t s = b r a n c h e s . n p o i n t s ; b r a n c h e s p a s s . v s i z e = b r a n c h e s . v s i z e ; b r a n c h e s p a s s . t s i z e = b r a n c h e s . t s i z e ;

g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ;

g l B u f f e r D a t a (GL ARRAY BUFFER, b r a n c h e s . v s i z e + b r a n c h e s . t s i z e + b r a n c h e s . n s i z e , NULL, GL STREAM DRAW) ;

GLvoid ∗ v b u f = g l M a p B u f f e r (GL ARRAY BUFFER, GL WRITE ONLY) ; g l B i n d B u f f e r (GL ELEMENT ARRAY BUFFER, b r a n c h e s v b o [ 1 ] ) ; g l B u f f e r D a t a (GL ELEMENT ARRAY BUFFER, b r a n c h e s . i s i z e , NULL,

GL STREAM DRAW) ;

GLvoid ∗ i b u f = g l M a p B u f f e r (GL ELEMENT ARRAY BUFFER, GL WRITE ONLY) ;

/∗ s a n i t y c h e c k ( v b u f != NULL && i b u f != NULL) ∗/

memcpy ( vbuf , b r a n c h e s . b u f f e r , b r a n c h e s . v s i z e + b r a n c h e s . t s i z e + b r a n c h e s . n s i z e ) ;

memcpy ( i b u f , b r a n c h e s . i b u f , b r a n c h e s . i s i z e ) ; g l B i n d B u f f e r (GL ARRAY BUFFER, b r a n c h e s v b o [ 0 ] ) ; GLboolean v s t a t u s = glUnmapBuffer (GL ARRAY BUFFER) ; g l B i n d B u f f e r (GL ELEMENT ARRAY BUFFER, b r a n c h e s v b o [ 1 ] ) ; GLboolean i s t a t u s = glUnmapBuffer (GL ELEMENT ARRAY BUFFER) ;

/∗ s a n i t y c h e c k ( v s t a t u s == GL TRUE && i s t a t u s == GL TRUE) ∗/

Related documents