Bezier
Animasjon
Fysikk
Grafikk
Børre Stenseth
Å tegne:>Trampoline

Trampoline

Hva

Dette modulen er bygd opp om en problemstilling som i korthet går ut på å lage en animasjon av en kule som spretter på en trampoline eller henger i en spiralfjær. Hensikten er å binde noe av den matematiske teorien og OpenGLs rutinebibliotek til et praktisk programmeringseksempel. Den relevante teorien er parametriske kurver og bezierflater.

hovedbilde

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.

udempet

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.

Canvasen nedenfor gir en mulighet for å eksperimentere med dempingsfaktoren, k, i uttrykket:

 f(t) = e-kt·cosinus(2·pi·t)
 
A float [0 < 1]:

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.

kulepos

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:

ovenfra

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.

frasiden

Hvordan skal vi sette H for å få en passende h ?

Vi tar utgangspunkt formelen for Bezierflaten.

bezier

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.

beregning1


   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?

[1] [2]

kode

  • Trampoline(JOGL/Netbeans): https://svnit.hiof.no/svn/psource/JOGL/bounce
  • Spiralfjær(JOGL/Netbeans): https://svnit.hiof.no/svn/psource/JOGL/spring
Referanser
  1. Computer Graphics: Principles and Practice in C (2nd Edition)James Foley, Andries van Dam, Steven K.Feiner, John F. Hughes1995Addison-Wesley0201848406
  1. Computer Graphics, with OpenGL (3nd edition)Donald Hearn, M. Pauline Baker2003Prentice Hall0130153907
Vedlikehold
Siste revidert juni 2004, Børre Stenseth
Å tegne:>Trampoline
til toppen