|
JLI Spieleprogrammierung
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
David Super JLI'ler
Alter: 39 Anmeldedatum: 13.10.2005 Beiträge: 315
Medaillen: Keine
|
Verfasst am: 08.11.2005, 20:54 Titel: Curved Surfaces & CO |
|
|
Hi!
Ein ganz interessantes Thema der 3D Programmierung sind wohl gekrümmte Oberflächen. Es gibt etliche Methoden diese zu berechnen, in diesem Tutorial nehmen wir die sogenannten Bézier Patches.
Fangen wir also (klein) an.
Wenn man sich einen Linienabschnitt betrachtet der von Punkt P0 zu P1 geht kann man jeden Punkt auf der Linie mit folgender Gleichung darstellen:
Q(t) = (1-t)P0 + (t)P1 => wobei t zwischen 0 und 1 liegt.
Aha, toll... nur wo ist hier die Kurve? Ganz einfach, nehmen wir einen weiteren Punkt hinzu P2 und interpolieren zwischen P0 und P1 sowie zwischen P1 und P2 so erhalten wir zwei weitere Punkte S0 und S1. Jetzt nurnoch zwischen S0 und S1 interpolieren und raus kommt der Punkt Q (abhängig von t) welcher jeden Punkt auf der Kurve repräsentiert.
In einer Gleichung ausgedrückt sieht das folgermaßen aus:
Q(t) = summe( i=0 bis 3, BiPi )
wobei die Basisfunktionen
B0 = ( 1-t )^2
B1 = 2t(1-t)
B3 = t^2
und P0-P2 die Kontrollpunkte sind.
So, zur Oberfläche ist es kein weiter Weg mehr, hier bilden die Kontrollpunkte ein 3x3 großes Gitter:
P0,0 P1,0 P2,0
P0,1 P1,1 P2,1
P0,2 P1,2 P2,2
Nun wegen die Kurven zwischen den Punkten P0,0 P0,1 P0,2 sowie P1,0 P1,1 P1,2 und P2,0 P2,1 und P2,2 berechnet. Danach die Kurve zwischen den daraus resultierenden Punkten und schon hat man seine gekrümmte Oberfläche.
Und nun noch ein bisschen Code:
CPP: | FVector3 Interpolate( FVector3 v[ 3 ], float t )
{
float b1, b2, b3;
FVector3 res;
b1 = ( 1-t )*( 1-t );
b2 = 2*t*( 1-t );
b3 = t*t;
res.x = v[ 0 ].x*b1+v[ 1 ].x*b2+v[ 2 ].x*b3;
res.y = v[ 0 ].y*b1+v[ 1 ].y*b2+v[ 2 ].y*b3;
res.z = v[ 0 ].z*b1+v[ 1 ].z*b2+v[ 2 ].z*b3;
return res;
}
void Subdivide( float tessx, float tessy, FVector3 **pts )
{
int i, j;
float t, f, subdivx, subdivy;
FVector3 p[ 3 ], *q;
subdivx = 1.0f / (tessy-1);
subdivy = 1.0f / (tessx-1);
*pts = new FVector3[ tessy*tessx ];
q = *pts;
for ( i = 0, t = .0f; i < tessy; i++, t += subdivx )
{
p[ 0 ] = Interpolate( &ctl_pts[ 0*3 ], t );
p[ 1 ] = Interpolate( &ctl_pts[ 1*3 ], t );
p[ 2 ] = Interpolate( &ctl_pts[ 2*3 ], t );
for ( j = 0, f = .0f; j < tessx; j++, f += subdivy )
{
*q = Interpolate( p, f );
q++;
}
}
}
|
ctl_pts ist ein Array welches die Kontrollpunkte enthält. tessx und tessy ist die Anzahl der Segmente in die Subdividiert werden soll.
Jetzt müssen nurnoch die einzelnen Polygone definiert werden:
CPP: | int GenerateIndexes( float tessx, float tessy, int **indexes )
{
int num_indexes, *p;
int p0, p1, p2, p3, u, v;
num_indexes = ( ( tessx-1 ) * ( tessy-1 ) ) * 6;
*indexes = new int[ num_indexes ];
p = *indexes;
for ( v = 0; v < tessx-1; v++ )
{
for ( u = 0; u < tessy-1; u++ )
{
p0 = u*tessx+v;
p1 = p0 + 1;
p2 = p0 + tessx + 1;
p3 = p0 + tessx;
*( p++ ) = p0;
*( p++ ) = p2;
*( p++ ) = p1;
*( p++ ) = p0;
*( p++ ) = p3;
*( p++ ) = p2;
}
}
return num_indexes;
}
|
Und losgehts mit dem Rendern des Patches:
CPP: | // ...
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, pts );
glDrawElements( GL_TRIANGLES, num_elements, GL_UNSIGNED_INT, indexes );
glDisableClientState( GL_VERTEX_ARRAY );
// ...
|
( Zum Schluss nicht vergessen den reservierten Speicher wieder freizugeben )
Und es klappt wirklich:
Screenshot 1
Screenshot 2
Screenshot 3
Verbesserungsvorschläge:
Auf Screenshot 3 ist zu erkennen, das nur in eine Richtung subdividiert wurde. Dies macht Sinn, da der Patch in eine Richtung "Planar" ist, also die Kontrollpunkte jeweils auf einer Höhe liegen.
Wenn man das also vor dem Subdividieren abtestet kann man einiges an Polygonen sparen.
Ich hoffe das ich das Thema "Bezierpatches" einigen von euch etwas näher bringen konnte. Zum Schluss noch ein Link auf ein PDF zum Thema, das ich mal geschrieben habe: Link
grüße |
|
Nach oben |
|
|
Jonathan_Klein Living Legend
Alter: 37 Anmeldedatum: 17.02.2003 Beiträge: 3433 Wohnort: Siegerland Medaillen: Keine
|
Verfasst am: 08.11.2005, 21:03 Titel: |
|
|
Kurze Frage: Was sind die Einsatzgebiete? Landschaften macht mal wohl meistens mit Heighmaps, man könnte das wohl vielleihct für Torbögen oder so machen, aber würde man die nicht eher modellieren? Naja, aber auf jeden fall coole Technik in der Demoscenen kann man das zum Platzsparen bestimmt gut benutezn, oder? _________________ https://jonathank.de/games/ |
|
Nach oben |
|
|
David Super JLI'ler
Alter: 39 Anmeldedatum: 13.10.2005 Beiträge: 315
Medaillen: Keine
|
Verfasst am: 08.11.2005, 21:54 Titel: |
|
|
Hi!
Der eine Vorteil ist halt, das man ziemlich viel Platz sparen kann, ein anderer ist das Unterteilungslevel. So kann man für bessere Rechner eine viel weichere Krümmung erzeugen als für leistungsschwächere. Außerdem können weit entfernte Patches mit weniger Details angezeigt werden.
Aktuell verwenden viele Spiele diese (und ähnliche Techniken). Doom3 beispielsweise nutzt Bézier Patches für Rohre, Torbögen und allerhand andere Objekte.
Und noch ein Paar bilder von Bézier Patches in Quake3 (übrigens mit meiner Implementation erzeugt ):
Screenshot 1
Screenshot 2
Screenshot 3
grüße |
|
Nach oben |
|
|
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
Powered by phpBB © 2001, 2005 phpBB Group Deutsche Übersetzung von phpBB.de
|