Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Jul 17, 2025 13:43

Foren-Übersicht » Programmierung » OpenGL
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 11 Beiträge ] 
Autor Nachricht
BeitragVerfasst: Di Mai 03, 2011 19:40 
Offline
DGL Member

Registriert: Di Mai 03, 2011 18:09
Beiträge: 5
Hallo Gemeinde,

ich bin neu auf dem Gebiet der Programmierung mit OpenGL und bin an einen Punkt gelangt, wo ich nicht ohne Hilfe weiterkomme. Dabei geht es um folgendes:

Ich habe eine BMP, in deren Größe ich ein Polygon-Gitter mit Hilfe von TRIANGLE-STRIP erzeugt habe. Anschließend habe ich die Höhenwerte (das BMP ist eine Karte) anhand Rotwerte eines Schwarzweißbildes der BMP ermittelt und somit das Gitter verformt, damit es plastisch wird. Meine follgende Aufgabe ist es, dass ich die Normalen zu jedem Knotenpunkt Berechne und dabei wird folgende Funktion vorgegeben: set_normal(int x, int y);

Mit Hilfe dieser Funktion und einer zusätzlichen - get_heightmap_value(int x, int y); -, bin ich in der Lage den genauen Knotenpunkt (Vertex) zu ermitteln. Allerdings fehlt mir an der folgenden Stelle die Idee, wie ich aus dem einen gegebenen Punkt an die beiden Vektoren komme, mit deren Hilfe ich die Normale über das Kreuzprodukt berechnen kann.

Hier mal die Funktion, wie sie bisher aussieht und keine richtigen Ergebnisse liefert...

Code:
  1.  
  2. // Calculate and set the normal for height map entry (x,y)
  3. void terrain::set_normal(int x, int y)
  4. {
  5.     //Vektor in X-Richtung
  6.     vec3d vec_x((double)x,0.0f, 0.0f);
  7.     //Vektor in Z-Richtung
  8.     vec3d vec_z(0.0f, 0.0f, get_heightmap_value(x, y));
  9.     //Kreuzprodukt
  10.     vec3d vec_normal = cross(vec_x, vec_z);
  11.     vec_normal.normalize();
  12.     //Übergabe an OpenGL
  13.     glNormal3f(-vec_normal.x(), -vec_normal.y(), -vec_normal.z());
  14. }
  15.  


Ich würde mich über Hilfe sehr freuen. Tips wären auch schon ganz hilfreich :)

Danke schonmal :)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Di Mai 03, 2011 20:15 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Schau dir mal diesen Shader an, der macht (unter anderem) genau das was du möchtest:
http://wiki.delphigl.com/index.php/shader_Terrain_GPU4

Code:
  1. // Höhe des zentralen Vertex
  2. height = terrainscale * texelFetch2DOffset(heightmap, coords, 0, ivec2(0,0)).a;
  3.  
  4. // berechne die 6 Vektoren zu den 6 umliegenden Vertices
  5. vec3 vector[6];
  6. vector[0] = vec3( 0.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 0, -1)).a, -1.0);
  7. vector[1] = vec3(-1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2(-1, -1)).a, -1.0);
  8. vector[2] = vec3(-1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2(-1,  0)).a,  0.0);
  9. vector[3] = vec3( 0.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 0,  1)).a,  1.0);
  10. vector[4] = vec3( 1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 1,  1)).a,  1.0);
  11. vector[5] = vec3( 1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 1,  0)).a,  0.0);
  12. for (int i=0; i<6; ++i) {
  13.     vector[i].y = terrainscale * vector[i].y - height;
  14. }
  15. // berechne die 6 Normalen zu der 6 umliegenden Dreiecke und summiere sie auf
  16. normal = cross(vector[5], vector[0]);
  17. for (int i=1; i<6; ++i) {
  18.     normal += cross(vector[i-1], vector[i]);
  19. }
  20. // Normalisieren damit das ganze wieder die Länge 1 hat
  21. normal = normalize(normal);


Die Variable coords ist dein (x,y). Das "texelFetch2DOffset(...).a" ist quasi dein get_heightmap_value, was an der Stelle coords + offset die Höhe zurück gibt. Der Offset ist das jeweils +1 oder -1....das was da mit ivec2 angegeben ist. Letztlich ist wird der Durchschnitt der Normalen der 6 umliegenden Dreiecke berechnet.

Tipp: Mal es dir auf einem Blatt Papier auf was da berechnet wird.

Du musst die Idee jetzt nur noch in deinem Code umsetzen :)


=> Das ganze gibt "weiche" Normalen....d.h. das Terrain sieht dann schön rund aus.

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Di Mai 03, 2011 20:47 
Offline
DGL Member

Registriert: Di Mai 03, 2011 18:09
Beiträge: 5
Erstmal vielen Dank! Habe das ganze jetzt so in meinem Programm liegen und das sieht schonmal wesentlich besser aus als mein Gefummel vorher! Hier mal mein Code:

Code:
  1.  
  2. // Calculate and set the normal for height map entry (x,y)
  3. void terrain::set_normal(int x, int y)
  4. {
  5.     //Vektoren zu den 6 umliegenden Knoten
  6.     vec3d vectors[6];
  7.         vectors[0] = vec3d((double)x,   get_heightmap_value(x,   y-1), (double)y-1);
  8.         vectors[1] = vec3d((double)x-1, get_heightmap_value(x-1, y-1), (double)y-1);
  9.         vectors[2] = vec3d((double)x-1, get_heightmap_value(x-1, y),   (double)y);
  10.         vectors[3] = vec3d((double)x,   get_heightmap_value(x,   y+1), (double)y+1);
  11.         vectors[4] = vec3d((double)x+1, get_heightmap_value(x+1, y+1), (double)y+1);
  12.         vectors[5] = vec3d((double)x+1, get_heightmap_value(x+1, y),   (double)y);
  13.     //Normalen berechnen und aufaddieren
  14.     vec3d normal = cross(vectors[5], vectors[0]);
  15.     for(int i=1; i<6; ++i)
  16.     {
  17.         normal += cross(vectors[i-1], vectors[i]);
  18.     }
  19.     //Normalenvektor normalisieren (Laenge = 1)
  20.     normal.normalize();
  21.     //übergeben der Normalen
  22.     glNormal3d(normal.x(), normal.y(), normal.z());
  23. }
  24.  


Allerdings wirkt das nicht wirklich "smooth", man sieht immernoch sehr stark, dass die beleuchtete Fläche grob in Vierecke aufgeteilt ist. Gibt es an meinem Code noch etwas zu verbessern?

Die Sache mit dem aufmalen hatte ich mir auch schon vorgenommen und das versucht umzusetzen, damit wurde mir dann klarer, was das Codefragment wirklich macht/machen soll.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Di Mai 03, 2011 21:12 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Deine vectors-Array enthält die umliegenden Vertices, nicht aber den Vektor vom zentralen Vertex zum jeweiligen Vertex. Du bildest ein Kreuzprodukt von Punkten....nicht von Vektoren! Der Vektor von Punkt A zu Punkt B ist "B - A".

Du musst von jedem Vertex in deinem vectors-Array nochmal den zentralen Vertex abziehen. Bei mir ist diese Subtraktion und auch die Skalierung mit dem Faktor terrainscale quasi integriert, da ich für die x und z Koordinate direkt -1 bzw. +1 einsetze. Nur für die y-Koordinate (bei dir ist das z) muss die Subtraktion und Skalierung noch durchgeführt werden. Der Code ist halt ein wenig optimiert.

So müsste es gehen:
Code:
  1. // Calculate and set the normal for height map entry (x,y)
  2. void terrain::set_normal(int x, int y)
  3. {
  4.     //Vektoren zu den 6 umliegenden Knoten
  5.     double height = get_heightmap_value(x, y);
  6.     vec3d vectors[6];
  7.         vectors[0] = vec3d( 0.0, get_heightmap_value(x  , y-1) - height, -1.0);
  8.         vectors[1] = vec3d(-1.0, get_heightmap_value(x-1, y-1) - height, -1.0);
  9.         vectors[2] = vec3d(-1.0, get_heightmap_value(x-1, y  ) - height,  0.0);
  10.         vectors[3] = vec3d( 0.0, get_heightmap_value(x  , y+1) - height,  1.0);
  11.         vectors[4] = vec3d( 1.0, get_heightmap_value(x+1, y+1) - height,  1.0);
  12.         vectors[5] = vec3d( 1.0, get_heightmap_value(x+1, y  ) - height,  0.0);
  13.     //Normalen berechnen und aufaddieren
  14.     vec3d normal = cross(vectors[5], vectors[0]);
  15.     for(int i=1; i<6; ++i) {
  16.         normal += cross(vectors[i-1], vectors[i]);
  17.     }
  18.     //Normalenvektor normalisieren (Laenge = 1)
  19.     normal.normalize();
  20.     //übergeben der Normalen
  21.     glNormal3d(normal.x(), normal.y(), normal.z());
  22. }



Des weiteren ist es möglich das deine Wahl der Dreiecke (das Quad muss ja irgendwie geteilt werden) nicht mit meiner übereinstimmt:
Dateianhang:
triangles.png


Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Di Mai 03, 2011 21:31 
Offline
DGL Member

Registriert: Di Mai 03, 2011 18:09
Beiträge: 5
Nochmals vielen Dank!

Habe deine Variante mal getestet (hatte vorher auch noch mit der Höhe als Element in der Erzeugung der Vektoren rumprobiert) und festgestellt, dass unsere beiden Ansätze identisch aussehen. Wird dann wohl mit der Reihenfolge der Knoten zu tun haben.

Ich kann dir ja mal meinen STRIP zeigen, den ich verwende:

Code:
  1.  
  2. glBegin(GL_TRIANGLE_STRIP);
  3.                 glVertex3f((double)x,   get_heightmap_value(x, y),     (double)y    );
  4.                 glVertex3f((double)x,   get_heightmap_value(x, y+1),   (double)y+1  );
  5.                 glVertex3f((double)x+1, get_heightmap_value(x+1, y),   (double)y    );
  6.                 glVertex3f((double)x+1, get_heightmap_value(x+1, y+1), (double)y+1  );
  7.             glEnd();
  8.  


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Di Mai 03, 2011 21:48 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Zitat:
dass unsere beiden Ansätze identisch aussehen.

Screenshot?

Zitat:
Wird dann wohl mit der Reihenfolge der Knoten zu tun haben.

Nein, das sollte kaum merkbare Einflüsse haben. (Zumindest sofern du ähnlich viele Dreiecke hast wie hier)

Zitat:
Ich kann dir ja mal meinen STRIP zeigen, den ich verwende:

Wo wird den da die Normale gesetzt? Ich würde sagen das muss so aussehen:
Code:
  1. glBegin(GL_TRIANGLE_STRIP);
  2. set_normal(x, y);
  3. glVertex3f((double)x,   get_heightmap_value(x, y),     (double)y    );
  4. set_normal(x, y+1);
  5. glVertex3f((double)x,   get_heightmap_value(x, y+1),   (double)y+1  );
  6. set_normal(x+1, y);
  7. glVertex3f((double)x+1, get_heightmap_value(x+1, y),   (double)y    );
  8. set_normal(x+1, y+1);
  9. glVertex3f((double)x+1, get_heightmap_value(x+1, y+1), (double)y+1  );
  10. glEnd();



P.S. Wenn du dein Terrain so renderst....wundere dich nicht wenn das EXTREM langsam ist. ;) Aber erstmal überhaupt zum laufen kriegen....dann optimieren.

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 04, 2011 06:28 
Offline
DGL Member

Registriert: Di Mai 03, 2011 18:09
Beiträge: 5
Du hattest recht, es lag am Setzen der Normalen bei der Erstellung des Gitters. Vielen Dank :)

Nun würde mich aber auch interessieren, wie man das ganze so rendern kann, dass das wesentlich schneller geht :) Ich habe da mal was von DisplayListen gelesen, könnte das damit zusammenhängen?


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 04, 2011 08:58 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Zitat:
Ich habe da mal was von DisplayListen gelesen, könnte das damit zusammenhängen?

Jein ;)

Optimierungen sind sortiert nach Effektivität, nach unten wird es besser:
  1. Du renderst immer nur zwei Dreiecke gleichzeitig! Benutze GL_TRIANGLES und rendere das gesamte Terrain in einem Rutsch. Jeder glBegin-Aufruf hat einen gewissen Overhead...also möglichst viel auf einmal rendern.
  2. Natürlich sollte man es vermeiden Dinge zu rendern die nicht sichtbar sind. Teile daher dein Terrain in Blöcke von 32x32 oder 64x64 Quads. Für jeden dieser Blöcke berechnest du im voraus eine BoundingBox und prüfst diese vor dem Rendern auf Sichtbarkeit. Bei einem großen Terrain sollte man diese BoundingBoxen zusätzlich in einer Baumstruktur anordnen (oder einer anderen für diesen Fall geeigneten Suchstruktur, etwa einem Grid)
  3. Packe jeden Block in eine Displayliste. Damit müssen deine Vertexdaten nicht in jedem Frame erneut auf die Grafikkarte hochgeladen werden. Das kostet verdammt viel Zeit!
  4. Vergiss die Displaylisten und nimm gleich VertexbufferObjects (VBO). Du kannst auch alle Blöcke in einen einzigen VBO packen und jeweils nur den entsprechenden Bereich rendern.
  5. Bei der Verwendung von VBOs kannst du nämlich einen Indexbuffer (auch Elementbuffer) benutzen. Das reduziert die Vertexmenge auf etwas mehr als 1/6. Allerdings wird zusätzlich der Indexbuffers mit 12 Byte pro Dreieck gebraucht. Lohnt sich vor allem wenn du noch Texturkoordinaten verwenden willst.
  6. Benutze einen Vertexshader der dir die Vertexdaten zur Laufzeit aus der Heightmap (als Textur) berechnet. Damit reduzierst du den benötigten Grafikspeicher nochmals massiv. Angenommen dein Terrain hätte eine Größe von 4096x4096 und jeder Vertex benötigt Position, Normale und zwei Sets von Texturkoordinaten (40 Bytes pro Vertex), dann würde selbst bei Verwendung eines Indexbuffers 640 MB Grafikspeicher benötigt. Dumm nur das viele Grafikkarten nur 512 oder gar 256 Mb haben! Mit dem Shader könntest du den Speicherverbrauch auf etwas mehr als 32 MB drücken. Das ganze ist hier beschrieben: http://wiki.delphigl.com/index.php/shader_Terrain_GPU4

Ausgehend vom genannten 4096x4096 Terrain mit 40 Byte pro Vertex:
Bei Punkt 1 liegt die Datenmenge bei 3.75 GB und wird in jedem Frame erneut zur Grafikkarte übertragen. Selbst mit der aller neuesten Hardcore-Grafikkarte wirst du nie mehr als eine handvoll FPS bekommen, der PCI-Express-Port gibt einfach nicht mehr her. Bei Punkt 6. haben wir das auf ca. 33 MB reduziert....die zudem permanent im Grafikspeicher liegen. Nur die eigentlichen Renderbefehle werden noch übertragen.

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 04, 2011 13:25 
Offline
DGL Member

Registriert: Di Mai 03, 2011 18:09
Beiträge: 5
Oh Wow!

Dann gibts in der Hinsicht ja noch jede Menge zu lernen :) Werde mir deine verlinkten Artikel mal duchlesen und mich schlau machen, was die ganze Sache angeht :)

Jetzt muss ich aber erstmal weiter an den mir gestellten Aufgaben basteln, damit ich das dann zeitnah abgeben kann.

Nochmals vielen Dank!


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Fr Mai 06, 2011 09:00 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Nov 08, 2010 18:41
Beiträge: 769
Programmiersprache: Gestern
schau außerdem mal hier rein das könnte sehr hilfreich sein...

_________________
Meine Homepage


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Fr Mai 06, 2011 09:26 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Zitat:
schau außerdem mal hier rein das könnte sehr hilfreich sein...

Ich behaupte jetzt nicht das komplett gelesen zu haben, aber ich sehe irgendwie keinen Zusammenhang mit einem 3D-Terrain? Wie da auch steht ist das eine Technik die man benutzt wenn man nur wenige Mhz Rechenleistung zur Verfügung hat....im Gegensatz zu den heute üblichen 2-3 GHz. Heute hat eine Kaffeemaschine ja schon bald mehr Rechenleistung. Relevanz?

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 11 Beiträge ] 
Foren-Übersicht » Programmierung » OpenGL


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 14 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.009s | 15 Queries | GZIP : On ]