Trampoline
For trampolina ønsker vi å lage en animasjon der en kule slippes ned på trampolina. Trampolina skal fjære og reflektere kula opp igjen. Kula vil etter hvert miste høyde og legge seg stille på en delvis nedsenket trampoline.
For fjæra slipper vi kula med en "slakk" fjær. Kula faller, stoppes av fjæra og kommer opp igjen. Kula skal ende hengende i en litt utstrekt fjær.
Animasjonene fordrer at vi løser tre problemer:
- Kulebevegelsen
- Framstilling av trampolina
- Framstilling av fjæra
Vi betrakter disse hver for seg nedenfor
Kulebevegelse
Dempet cosinus
Vi ønsker en basisbevegelse der kula skal bevege seg opp og ned. Den mest nærliggende løsningene er en parametrisk cosinus. Uttrykket
z= z_start· cos(2· pi·t);
vil gi en svingende bevegelse når t økes i steg på dt.
Vi kan starte animasjonen med t initialisert til 0.
Vi bruker denne svingende bevegelsen som utgangspunkt og fokuserer på hvordan vi skal få dempet svingningene. Animasjonen skal jo være slik at kula mister høyde for hver periode og til slutt legge seg til ro på en delvis nedsenket trampoline.
En omhyllingskurve som reduserer amplituden på svingningene kan vi framstille som en eksponentialfunksjon:
f(t) = e-kt
Vi velger e (= 2.302585093) som grunntall av rene bekvemlighetshensyn, fordi vi har en tilgjengelig funksjon exp() som beregner denne funksjonen. For modelleringen spiller dette ingen rolle. Vi kan alltids regne oss fra et grunntall til et annet.
Appleten nedenfor gir en mulighet for å eksperimentere med dempingsfaktoren, k, i uttrykket:
f(t) = e-kt·cosinus(2·pi·t)
Vi kan bestemme konstanten k, dempingsfaktoren, analytisk. Vi setter f.eks. som betingelse at kulas utslag skal være redusert til det halve etter tre perioder, tre svingninger.
f(3) = e-3·2·pi·k = 0.5
Vi tar den naturlige logaritmen på begge sider av likhetstegnet og får:
k=-ln(0.5)/(3·2·pi) k=0.03 (avrundet)
Vi lar dette være en foreløpig og implementerbar beskrivelse av kulas bevegelse.
Krefter og masse
Vi velger et alternativt utgangspunkt i betraktningen av kula. Vi ser på sammenhengen mellom masse, krefter, akselerasjon, hastighet og posisjon. Vi baserer resonnementet på følgende:
K=m·a | Dersom kula har masse m og blir utsatt for en kraft k, får den en akselerasjon a. |
dv=a·dt | Hastigheten til kula, v, endrer seg dv dersom den blir aksellerert i tiden dt. dt er det tidssteget som går mellom hver beregning og uttegning i animasjonen. |
dz=v·dt | Posisjonen til kula, z, endrer seg dz dersom hastigheten i tiden dt er v. |
Hvis vi betrakter kula er det to krefter som er aktuelle:
- Tyngdekraften virker alltid på kula. Den kan beskrives som g·m, der m er kulas masse og g er den universelle konstanten som beskriver tiltrekningen mellom to legemer. g er 9.81 m/s2
- Den andre kraften er trampolinens protest mot å bli trykket ned. Denne kraften er avhengig av nettets stivhet. Vi lar kraften øke proporsjonalt med nedtrykket.
Til sammen kan vi da skrive følgende kraftregnskap for kula:
if(z>=0) sumK=m*g else sumK=m*g+m*s*z
Der vi bruker z for kulas posisjon, og vi lar s angi nettets motstand mot å bli nedtrykt.
Algoritmisk kan vi da skrive beregningen av kulas posisjon i et bestemt tidspunkt slik:
a=g; if(z<0) a+=s*z/m; v+=a*dt; v-=f*v; z+=v*dt;
Der vi har innført en dempingsfaktor f, som en lineær reduksjon av hastigheten.
Det er mange muliugheter for å raffinere denne modelleringen. s og f trenger f.eks. ikke være lineære.
Trampoline
Trampolinen er kvadratisk. Hele resonnementet nedenfor er basert på at trampolina når den ligger flatt ligger i xy-planet, og den positive z-aksen peker opp mot kulas utgangsstilling.
Vi vil lage trampolinen som en bezierflate. Vi definerer et sett med kontrollpunkter som beskriver trampolina i hvilestilling, uten belastning:
// ctrlpoints for trampoline // 4 X 4 points, 3 coordinates each ctrlpoints = new float[] { -1.5f, -1.5f, 0.0f, -0.5f, -1.5f, 0.0f, 0.5f, -1.5f, 0.0f, 1.5f, -1.5f, 0.0f, -1.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 1.5f, -0.5f, 0.0f, -1.5f, 0.5f, 0.0f, -0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 1.5f, 0.5f, 0.0f, -1.5f, 1.5f, 0.0f, -0.5f, 1.5f, 0.0f, 0.5f, 1.5f, 0.0f, 1.5f, 1.5f, 0.0f };
Altså en beskrivelse som sett lang z-aksen er slik:
Merk at alle z-verdiene er 0.0, dvs trampolinen ligger flatt i xy-planet. Rutenettet er kvadratisk med sider 0.5.
Når kula treffer trampolinen skal den "gi etter" og bøye seg ned. Vi vil gjøre dette ved å la de fire midterste kontrollpunktene i flaten få negative verdier. Problemet vårt er å finne ut hvor langt ned vi må trekke disse kontrollpunktene for at bezierflaten skal passe nøyaktig til kulas posisjon.
Hvordan skal vi sette H for å få en passende h ?
Vi tar utgangspunkt formelen for Bezierflaten.
Betingelsen vi setter opp er:
z(0.5,0.5) = h
Igjen forholder vi oss til at alle randpunktene skal ha z-verdi 0, det vil si alle kontrollpunkter der koordinatverdiene har i eller j lik 0 eller 3. Vi lar de fire "senter-punktene" ha verdien H. Vi forutsetter videre ut fra rene symmetribetraktninger at bunnpunktet inntreffer når v og u begge er lik 0.5.
H=1.778 · h
Vi realiserer dette i algoritmen slik:
/////////////////////////////////////// // set nets controlpoints accordingly // adjust z-values of the 4 "center" points float h=m_SphereZ-m_SphereR; ctrlpoints[18-1]= ctrlpoints[21-1]= ctrlpoints[30-1]= ctrlpoints[33-1]=Math.min(1.778f*h,0.0f);
og
gl.glMap2f(GL.GL_MAP2_VERTEX_3,0.0f,1.0f,3,4,0.0f,1.0f,12,4,ctrlpoints,0); gl.glEnable(GL.GL_MAP2_VERTEX_3); gl.glEnable(GL.GL_AUTO_NORMAL); gl.glEnable(GL.GL_NORMALIZE); gl.glMapGrid2f(20,0.0f,1.0f,20,0.0f, 1.0f);
Spiralfjær
Kula
Animasjon av ei kule som henger i ei spiralfjær kan modelleres på nesten samme måte som for trampoline løsningen. Vi kan bruke cosinusløsningen som den er utledet ovenfor, men vi får litt andre forhold når vi ser på masse og aksellerasjon.
Hvis vi betrakter kula er det to krefter som er aktuelle:
- Tyngdekraften virker alltid på kula. Den kan beskrives som g·m, der m er kulas masse og g er den universelle konstanten som beskriver tiltrekningen mellom to legemer. g er 9.81 m/s2
- Den andre kraften er fjæras motvilje mot å bli strekt. Vi lar denne kraften øke som en funksjon av strekklengden.
Kraftregnskapet for kula:
sumK=m*g-m*f(z)
Der vi bruker z for kulas posisjon, og vi lar f(z) angi fjæras motstand mot å bli strekt. Vi kan ekserimentere med ulike funksjoner, f
Algoritmisk kan vi da skrive beregningen av kulas posisjon i et bestemt tidspunkt slik:
a=g-m*f(z)/m; v+=a*dt; v-=f*v; z+=v*dt;
Spiralfjæra
Selve fjæra kan vi beskrive som en spiral etter følgende parametriske uttrykk:
y(t)=r·sin(2·pi·t) x(t)=r·cos(2·pi·t) z(t)= k·t
Der r er radien og k er avstanden mellom to ringer i spiralen. Dersom vi lar t løpe fra 0 til n, beskriver vi n "vindinger" på spiralfjæra. Dersom fjæra strekkes vil k øke.
Dersom vi skal ta hensyn til at spiralens radius minker når den strekkes, må r modifiseres som en funksjon av k. En kodeskisse av uttegningen av spiralfjæra kan se slik ut:
float topOfHelix=m_SphereZ_Start+m_SphereR+0.2f; float incr=(topOfHelix-m_SphereZ-m_SphereR-0.2f)/circles; // reduce radius according to stretch float topi=(float)(2*Math.PI); float actualR=R-incr/topi; gl.glLineWidth(2.0f); gl.glBegin(gl.GL_LINE_STRIP); float t=0.0f; float dt=0.01f; while(t< circles) { gl.glVertex3f((float)(actualR*Math.cos(t*topi)), (float)(actualR*Math.sin(t*topi)), topOfHelix-incr*t); t+=dt; } // finally to centre and down to sphere gl.glVertex3f(0.0f,0.0f,topOfHelix-incr*t); gl.glVertex3f(0.0f,0.0f,m_SphereZ); gl.glEnd();
Implementasjon
Trampolina og spiralfjøra er implementert som to prosjekter. De er bygget på samme arktiktur og det som skiller dem er uttegning av modellen, Bezier eller fjør.
Timing er styrt fra en Timerklasse som starter en refresh av OpenGL-panelet ved definerte tidspunkter. begge prosjektene har to bevegelsesmodeller som alternativer. Fo fjøra er det slik:
public void display(GLAutoDrawable drawable) { GL gl = drawable.getGL(); showScene(gl); // do increments here,calculate spheres new position: m_SphereZ ////////////////// // Alternative1 : acceleration version // Experiment with stiffness and friction accelaration=-g-stiffness*m_SphereZ/mass; speed+=accelaration*dt; m_SphereZ+=speed*dt; if(speed>0) speed-=friction*Math.abs(speed); else speed+=friction*Math.abs(speed); /* ////////////////////////////////////// // Alternative2 : cosinus version // Experiment with numeric values on fk aniTime+=dt; double t=2.0f*Math.PI*aniTime; m_SphereZ=(float)(m_SphereZ_Start*Math.exp(-fk*t)*Math.cos(t)); */ }
For trampolina
public void display(GLAutoDrawable drawable) { GL gl = drawable.getGL(); showScene(gl); // do increment, calculate spheres new position: m_SphereZ ////////////////// // Alternative1 : acceleration version // Experiment with stiffness and friction if(m_SphereZ>0.0f) accelaration=-g; else accelaration=-g-stiffness*m_SphereZ/mass; speed+=accelaration*dt; m_SphereZ+=speed*dt; if(speed>0) speed-=friction*Math.abs(speed); else speed+=friction*Math.abs(speed); /* ////////////////////////////////////// // Alternative 2 : cosinus version // Experiment with numeric values on fk aniTime+=dt; double t=2.0f*Math.PI*aniTime; m_SphereZ=(float)(m_SphereZ_Start*Math.exp(-fk*t)*Math.cos(t)); */ /////////////////////////////////////// // set nets controlpoints accordingly // adjust z-values of the 4 "center" points float h=m_SphereZ-m_SphereR; ctrlpoints[18-1]= ctrlpoints[21-1]= ctrlpoints[30-1]= ctrlpoints[33-1]=Math.min(1.778f*h,0.0f); }
Bra nok ?
Hvis du analyserer modelbeskrivelsen og kjører de to demoene er det flere ting det kan stilles spørsmål ved. Vi har vært ganske upresise når det gjelder demping av svingningene. Vi har brukt en lineær motstand mot bevegelse både i trampolina og i fjøra. Du vil også se at modelleringen av trampolina ikke er tilstrekkelig god ved store utslag. Vår enkle modell med 4 sentrert kontrolpunkter er begrensende.
Spørsmålet som må besvares er: Er det godt nok til å lage en illusjon av "naturlig" bevegelse? Eller må vi jobbe mer med parametrene i modellen?