Motion-Blur: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Realisierung in OpenGL und Beispielcode)
K (Alter Algorithmus hatte ein paar Schwächen)
Zeile 32: Zeile 32:
 
# (Bildschirm-)Textur erzeugen [winwidth x winheight] und initialisieren.
 
# (Bildschirm-)Textur erzeugen [winwidth x winheight] und initialisieren.
 
# Bildschirm und Tiefenpuffer löschen
 
# Bildschirm und Tiefenpuffer löschen
# Textur ( im [[glOrtho | Orthomodus]] ) geblendet auf den Bildschirm zeichnen ( Faktor 0.98 abhängig von Framerate, Blending mit Farbe schwarz, nicht in Depth-Buffer schreiben )
 
 
# Szene rendern
 
# Szene rendern
 +
# Textur ( im [[glOrtho | Orthomodus]] ) geblendet auf die Szene zeichnen ( mit Alphawert von z.B. 0.98, abhängig von Framerate, nicht in Depth-Buffer schreiben )
 
# [[glCopyTexSubImage2D]] und weiter bei Schritt 2.
 
# [[glCopyTexSubImage2D]] und weiter bei Schritt 2.
  
Zeile 63: Zeile 63:
 
{
 
{
 
   ...
 
   ...
  beginMotionBlur();
 
 
 
   //Szene rendern
 
   //Szene rendern
  
 +
  beginMotionBlur();
 
   endMotionBlur();
 
   endMotionBlur();
 
   ...
 
   ...
Zeile 73: Zeile 72:
 
void beginMotionBlur()
 
void beginMotionBlur()
 
{
 
{
    //Motion-Faktor als Farbe übergeben, Wert abhängig von Framerate
 
    glColor3d( 0.98, 0.98, 0.98 );
 
    glDepthMask( GL_FALSE );
 
 
     glEnable( GL_TEXTURE_2D );
 
     glEnable( GL_TEXTURE_2D );
 +
    glEnable( GL_BLEND );
 +
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
 +
 +
    glDepthMask( false );
 +
 +
    //Motion-Faktor als Alpha-Faktor übergeben, Wert abhängig von Framerate ( z.B. 0.98 )
 +
    glColor4d( 1.0f, 1.0f, 1.0f, decay );
 +
   
 +
    //Motion-Textur über Szene blenden
 
     glBindTexture( GL_TEXTURE_2D, theMotionTex );
 
     glBindTexture( GL_TEXTURE_2D, theMotionTex );
 
+
    screenQuad( winwidth, winheight );
     screenQuad();
+
      
+
     glDepthMask( true );
 +
   
 +
     glDisable( GL_BLEND );
 
     glDisable( GL_TEXTURE_2D );
 
     glDisable( GL_TEXTURE_2D );
    glDepthMask( GL_TRUE );
 
 
}
 
}
  
Zeile 136: Zeile 142:
  
 
{|
 
{|
|[[Bild:Motion_blur.png|framed|Szene mit aktiviertem Motion-Blur.]]
+
|[[Bild:Motionblur.png|framed|Szene mit aktiviertem Motion-Blur.]]
 
|}
 
|}
 +
 +
== Demo ==
 +
 +
[http://algoria.de/opengl/demos/motionblur.jnlp Demo] ''(mit Quellen)''

Version vom 9. Mai 2009, 16:47 Uhr

Motion Blur ist üblicherweise kein Effekt den man macht weil er schön ist, sondern ein Effekt der bei der Übertragung von digitalen Bildern zum menschlichen Auge eine Art Vermittlungsrolle spielt.

Biologische Grundlagen

Das menschliche Auge nimmt etwa 25 Bilder je Sekunde wahr. Im Unterschied zur Computergrafik sind dies jedoch keine Momentaufnahmen sondern eine Art Aufsummierung aller Lichtreize die während dieser 1/25 Sekunde entstanden sind.

Ein leuchtender Punkt

Nehmen wir als Beispiel einen schwarzen Hintergrund. Über diesen schwarzen Hintergrund wandert (verhältnismäßig schnell) ein Licht. In der Computergrafik wenn wir beispielsweise 60 Frames pro Sekunde darstellen können würden wir 60 Momentaufnahmen darstellen, wobei in diesen Momentaufnahmen die Bewegung des Lichtpunktes keine Rolle spielen würde. Das menschliche Auge hingegen erhält nicht 25 Momentaufnahmen, sondern eine analoge Darstellung des Lichtsignales. Dieses analoge Lichtsignal wird nun (wie bereits erwähnt) während der gesamten 1/25 Sekunde aufsummiert und als solches an das Gehirn übertragen. Somit sehen wir nicht einzelne Momentaufnahmen des Lichtes sondern den gesamten Weg den das Licht zurückgelegt hat.

Wenn wir die Tatsache annehmen, dass wir nur 25 Bilder je Sekunde sehen, so können wir dies durch ein sehr einfaches Experiment nachvollziehen:

Begebt euch in einen möglichst dunklen Raum und vollführt mit einem Licht (zB Feuerzeug) verhältnismäßig schnelle Bewegungen. Ihr könnt nun den gesamten Weg sehen den das Licht zurückgelegt hat.

Technische Umsetzung

Filme

Wenn ihr euch einen Film anseht, so scheinen die Bewegungen üblicherweise realer als in einem Computerspiel. Wenn ihr euch ein Standbild eines Filmes anseht so wird offensichtlich weshalb dies der Fall ist. Der Film besitzt üblicherweise zwar nur 25 Bilder je Sekunde jedoch besitzen diese Bilder (gleich zum menschlichen Auge) die aufsummierte Helligkeit der 1/25 Sekunde, was in einem Standbild durch Streifen sichtbar wird. Diese Streifen kann man nun als Idealfall von Motion Blur ansehen.

Computergrafik

In der Computergrafik stellt man üblicherweise Standbilder dar. Es wäre enorm aufwändig die Berechnungen durchzuführen welche Farbe und welche Helligkeit ein Pixel haben würde, würde es die letzte 1/25 Sekunde darstellen und nicht nur eine Momentaufnahme. Glücklicherweise ist der Mensch nicht ganz so wählerisch und begnügt sich auch mit etwas besseren Darstellungen seiner Wirklichkeit, somit ist es um einiges besser wenn man beispielsweise 60 Momentaufnahmen je Sekunde besitzt als wenn man nur 25 Momentaufnahmen besitzt.

Motion Blur in der Praxis

Motion Blur ist also ein Effekt der das menschliche Auge möglichst zufriedenstellen soll. Bei verhältnismäßig langsamen Geschwindigkeiten (oder aber bei sehr hohen Frames je Sekunde) hat Motion Blur kaum bis keine Auswirkungen. Erst bei höheren Geschwindigkeiten kann Motion Blur sinnvoll eingesetzt werden. Hier geht es jedoch teilweise nicht nur darum allein das Standbild durch grafische Effekte zu verbessern, sondern im Idealfall geht es darum für jedes Pixel die vergangene 1/25 Sekunde zu beachten.

In Filmen sehen wir Motion Blur dann, wenn enorm hohe Geschwindigkeiten dargestellt werden sollen (annähernd Lichtgeschwindigkeit in Science Fiction Filmen). Obwohl ich weder ein Physiker noch ein Biologe bin, kann ich guten Gewissens behaupten, dass wir ein Raumschiff welches mit (annähernd) Lichtgeschwindigkeit an uns vorbei fliegt wohl so gut wie gar nicht sehen würden, da es nur einen sehr sehr kleinen Bruchteil einer 1/25 Sekunde sichtbar wäre ... einzige Ausnahme wäre natürlich wenn es enorm hell wäre, wodurch dieser sehr kleine Bruchteil viel bei der Aufsummierung der 1/25 Sekunde ausmachen könnte.

In der Computergrafik bezieht sich Motion Blur üblicherweise ausschließlich auf Momentaufnahmen wo die aktuelle Geschwindigkeit beachtet wird, oder aber die vorangegangenen Frames ebenfall einen Einfluss besitzen.

Realisierung in OpenGL

Standardmässig wurde Motion Blur in OpenGL mit dem Akkumulationspuffer realisiert, der jedoch nicht sehr schnell ist. Eine schnellere Methode arbeitet mit einer Bildschirmtextur und funktioniert folgendermassen :

  1. (Bildschirm-)Textur erzeugen [winwidth x winheight] und initialisieren.
  2. Bildschirm und Tiefenpuffer löschen
  3. Szene rendern
  4. Textur ( im Orthomodus ) geblendet auf die Szene zeichnen ( mit Alphawert von z.B. 0.98, abhängig von Framerate, nicht in Depth-Buffer schreiben )
  5. glCopyTexSubImage2D und weiter bei Schritt 2.

Beispielcode ( C )

void initMotionBlur()
{
    //Textur in Bildschirmgrösse
    int texSize = WINWIDTH * WINHEIGHT * 3;
    float* motionTex = new float[texSize];

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 
    glGenTextures( 1, &theMotionTex );
    glBindTexture( GL_TEXTURE_2D, theMotionTex );
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WINWIDTH, WINHEIGHT, 0, GL_RGB, GL_FLOAT, motionTex);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );   
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );  
    
    //Modulate-Modus, um Textur mit glColor zu mischen
    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

    delete [] motionTex;
}

...

void display()
{
   ...
   //Szene rendern

   beginMotionBlur();
   endMotionBlur();
   ...
}

void beginMotionBlur()
{
    glEnable( GL_TEXTURE_2D );
    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	 
    glDepthMask( false );
	 
    //Motion-Faktor als Alpha-Faktor übergeben, Wert abhängig von Framerate ( z.B. 0.98 )
    glColor4d( 1.0f, 1.0f, 1.0f, decay );
	    
    //Motion-Textur über Szene blenden
    glBindTexture( GL_TEXTURE_2D, theMotionTex );
    screenQuad( winwidth, winheight );	
	 
    glDepthMask( true );
	    
    glDisable( GL_BLEND );
    glDisable( GL_TEXTURE_2D );
}

void endMotionBlur()
{
    //Bildschirm zurück in Textur kopieren
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, theMotionTex );
    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, WINWIDTH, WINHEIGHT);
    glDisable( GL_TEXTURE_2D );
}

void screenQuad()
{
    beginInfoScreen( WINWIDTH, WINHEIGHT );
        glBegin(GL_QUADS);
            glTexCoord2i( 0, 0 );
            glVertex2i( 0, 0 );
            glTexCoord2i( 1, 0 );
            glVertex2i( WINWIDTH, 0 );
            glTexCoord2i( 1, 1 );
            glVertex2i( WINWIDTH, WINHEIGHT );
            glTexCoord2i( 0, 1 );
            glVertex2i( 0, WINHEIGHT );
        glEnd();
    endInfoScreen();
}

void beginInfoScreen( int winwidth, int winheight )
{
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0, winwidth, 0, winheight);
                
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
}

void endInfoScreen()
{
    glPopMatrix();
   
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
   
    glMatrixMode(GL_MODELVIEW); 
}
...
Szene mit aktiviertem Motion-Blur.

Demo

Demo (mit Quellen)