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

Aktuelle Zeit: Di Mai 14, 2024 11:13

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



Ein neues Thema erstellen Auf das Thema antworten  [ 16 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Autor Nachricht
BeitragVerfasst: So Jan 12, 2014 00:36 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Hallo,
neulich ist mir die Frage in den Sinn gekommen, wie man eigentlich sinnvoll Attribute speichert, die nicht per Vertex und auch nicht per Fragment anfallen, sondern per Primitive. Dabei denke ich insbesondere an die (Bi-)Tangente beim Bumpmapping. Im Gegensatz zur Normale sollte man diese nicht pro Vertex speichern und dann über die Primitive interpolieren. Mir sind im Grunde drei Möglichkeiten eingefallen:
  • Pro Vertex speichern und dabei in Kauf nehmen, dass man deutlich mehr Vertices speichern muss, als ohne Tangente. (Man braucht mehrere Vertices, obwohl Position, Normale und Texturkoordinate gleich sind.)
  • In einer Textur speichern und per texelFetch mit gl_PrimitiveID als Koordinate darauf zugreifen. Ein Texturzugriff pro Fragment dürfte aber langsamer sein, als etwas pro Vertex mitzugeben.
  • Nicht speichern, sondern on-the-fly im Geometryshader berechnen. Bei Tangenten geht das, bei anderen Attributen möglicherweise nicht. Auch hier stellt sich natürlich die Frage, wie viel Leistung diese Berechnungen kosten.

Gibt es bessere Alternativen? Was ist der übliche Weg?

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 00:51 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Die Texturlösung würde ich ausschließen. Das ist die komplexste Lösung und hat ziemlich viel Overhead. Zusätzlich sind Texturzugriffe außerhalb des Fragmentshaders scheinbar ziemlich langsam langsam.
Ich würde sie dem Vertex mitgeben. Man muss aber auch bedenken, dass sich mehrere Primitiven einen Vertex teilen können. Eine Option wäre es dann, einer Primitive immer die Daten des ersten Vertices zuzuweisen. Dadruch sinkt die Menge doppelter Daten noch weiter.

Allerdings, meinst du nicht, dass es auch sinnvoll sein könnte auch die Tangente zu interpolieren?
Ich bin mir der Mathematik dahinter allerdings gerade nicht mehr sicher.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 11:37 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Wenn man Tangenten interpoliert, passiert sowas wie im Anhang. In Klammern sind die Texturkoordinaten der Vertices. Rot und Grün sind die Tangenten, die pro Dreieck berechnet wurden und in Richtung der zweiten Texturkoordinate zeigen. Wenn man das pro Vertex speichert, muss man interpolieren. An der gemeinsamen Kante der Dreiecke wäre die Tangente dann der gelbe Pfeil. Da die Tangenten zusammen mit dem Normalenvektor die TBN Matrix bilden, mit der im Fragmentshader die Normalen aus der Normalmap transformiert werden, führt das zu falschen Ergebnissen.

Edit: Ganz fatal wird es, wenn die Tangenten zweier aneinander grenzender Dreiecke in jeweils entgegengesetzte Richtungen zeigen. Dann heben sie sich bei der Interpolation ganz weg und man würde eine resultierende Tangente (0,0,0) im Vertex speichern. Sowas kommt vor, wenn man Texturen an einer Kante entlang spiegelt (macht man z.B. manchmal bei Gesichtern).

OpenglerF hat geschrieben:
Zusätzlich sind Texturzugriffe außerhalb des Fragmentshaders scheinbar ziemlich langsam
Meinst du, es wäre langsamer, einen Texturefetch (pro Primitive) im Geometryshader auszuführen, als viele (1 pro Fragment) im Fragementshader?


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

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Zuletzt geändert von glAwesome am So Jan 12, 2014 13:14, insgesamt 1-mal geändert.

Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 12:30 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Zitat:
Meinst du, es wäre langsamer, einen Texturefetch (pro Primitive) im Geometryshader auszuführen, als viele (1 pro Fragment) im Fragmentshader?

Ja.
Ich würde auf Texturzugriffe so weit wie möglich verzichten. Ich denke, dass in einer durchschnittlichen Anwendung ein Texturzugriff deutlich stärker ins Gewicht fällt als ein paar Operationen. Nicht zu vergessen der zusätzliche Verwaltungsaufwand, die zusätzliche Indirektion und die vielen Daten bei den Vertices um auf die Tangente zu referenzieren. Das ist mit Sicherheit keine gute Idee.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 12:40 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
OpenglerF hat geschrieben:
Nicht zu vergessen der zusätzliche Verwaltungsaufwand, die zusätzliche Indirektion
Das verstehe ich nicht ganz. Warum ist ein Zugriff auf eine Textur teurer als auf einen anderen Buffer, wie z.B. den VBO, der ebenfalls im Grafikspeicher liegt? Es geht in diesem Fall nicht um einen Aufruf von texture sondern von texelFetch - es soll also keine Filterung stattfinden.

OpenglerF hat geschrieben:
die vielen Daten bei den Vertices um auf die Tangente zu referenzieren
Ich weiß nicht, was du meinst. Man bräuchte eigentlich nur gl_PrimitiveID, um zu bestimmen, in welchem Texel sich die Tangente befindet.

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 12:55 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Zitat:
Es geht in diesem Fall nicht um einen Aufruf von texture sondern von texelFetch

So weit ich gelesen habe, ist bei den aktuellen Karten der Texelfetch als Spezialfall des normalen Samples implementiert und die Funktion daher nicht wesentlich besser als ein Sampler.
Es belastet einfach den Cache, wenn die Daten nicht bei den Anderen liegen, sondern irgendwo anders.
Auch auf der CPU ist es schon lange so, dass ein ein einziger Cachemiss deutlich mehr Zeit braucht, als eine ganze Reihe von Instruktionen. Auf der GPU ist es sicher nicht anders. Außerdem ist da immer das schnell, wofür die GPU optimiert ist. Und das sind eben nicht Texturzugriffe im Geometryshader.
Dann gibt es noch ein Texturgrößenlimit, es können auf meiner Grafikkarte zum Beispiel nur 2^13 Texel nebeneinander liegen, in einer 2D Textur.
Dann müssen bei Modellwechseln nicht nur die Buffer gewechselt werden sondern es muss auch eine neue Textur gebunden werden.

Zitat:
Ich weiß nicht, was du meinst. Man bräuchte eigentlich nur gl_PrimitiveID, um zu bestimmen, in welchem Texel sich die Tangente befindet.

Ok, das wäre eine Möglichkeit. Allerdings bin ich mir gerade nicht sicher, ob garantiert ist, dass die PrimitiveID immer dem entsprechenden Primitiven zugeordnet wird. Und mal nur einen Teil eines Modells zeichnen kann man dann auch vergessen. Genauso wie die Technik, mehrere Modelle mit einen DrawCall zu zeichnen, in dem man sie in einen VBO zusammenpackt.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 13:05 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Also läuft es am Ende wohl darauf hinaus, die Tangenten im Geometryshader zu berechnen. Ich wusste, der Tag würde kommen, an dem ich Geometryshader-Support in meine Shader-Klasse einbauen muss... :mrgreen:

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 21:04 
Offline
DGL Member
Benutzeravatar

Registriert: So Sep 26, 2010 12:54
Beiträge: 238
Wohnort: wieder in Berlin
Programmiersprache: Englisch
Hmm?

die Tang-Enten kannst Du getrost auf der CPU rechnen lassen, man rechnet diese auch in der Regel nur ein einziges Mal. Da ist absolut kein Geometryshader nötig.

Kann man schön in die Contentpipeline einbauen, wenn man eine hat.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 22:35 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Wie ich die Tangenten auf der CPU berechne, weiß ich. Kannst du mir auch sagen, wie ich die gescheit an den Shader weitergebe? Das war nämlich die eigentliche Frage. :)

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 22:40 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Weitergeben am desten ganz einfach als Vertexattribut. :wink:


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 23:06 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Aber den Ansatz hatten wir doch schon am Anfang dieses Themas abgehandelt. Entweder man muss interpolieren - was zu falschen Ergebnissen führt - oder man verdoppelt (ungefähr) die Anzahl der Vertices. Beides finde ich nicht so toll. Und wenn man mal überlegt, welcher Rechenaufwand im Shader anfallen würde, macht es wahrscheinlich auch keinen so großen Unterschied mehr.

Tangente als Vertexattribut:
-> Im Vertexshader müssen zusätzlich Tangente und Bitangente mit der Normalmatrix transformiert und danach normalisiert (Wurzel!) werden werden.
-> Geometryshader fällt weg.

Tangente im Geometryshader berechnen:
-> Im Vertexshader kein zusätzlicher Aufwand.
-> Im Geometryshader müssen die beiden Tangenten berechnet werden. Wenn man das wie im Artikel TBN Matrix macht, muss dabei keine Wurzel gezogen werden. In GLSL sieht das so aus:
Code:
  1. vec3 GetTangent(vec3 A, vec3 B, vec3 C,  vec2 Auv, vec2 Buv, vec2 Cuv)
  2. {
  3.   float Quotient = (Auv.y - Cuv.y)/(Buv.y - Cuv.y);
  4.   vec3 D   = C   + (B  -C)   * Quotient;
  5.   vec2 Duv = Cuv + (Buv-Cuv) * Quotient;
  6.   return (D-A)/(Duv.x - Auv.x);
  7. }

Das ganze zwei mal (einmal für jede Tangente) im Geometryshader sollte jetzt nicht sooo einen großen Framerateneinbruch bescheren. Zumal der Vertexshader nicht so oft ausgeführt werden müsste, wie beim anderen Ansatz (da weniger Vertices).

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jan 12, 2014 23:36 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Ja, wahrscheinlich ist das die Lösung.
Allerdings als Vertexattribut wäre es auch sinnvoll möglich. Ich meine so wie im Anhang. Man verteilt die Primitivstartvertices einfach gleichmäßig über das Gitter wie im Anhang. Klar, das dass nur dann wirklich funktioniert, wenn jedes Vertex in ~3 Polygone verwendet wird. Dann stellt man die Interpolation für das Vertexattribut einfach aus("flat") und schon hat man ohne Geoshader die Werte im Fragmentshader.

Deine Geoshaderlösung scheint mir allerdings noch einfacher zu sein.


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


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jan 13, 2014 00:05 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Ui, ich muss zugeben, dass mir der Umgang mit dem flat-Qualifier noch unbekannt war. Ich nehme an, die Nummerierung in deinem Bild gibt für jedes Dreieck an, in welcher Reihenfolge seine Eckpunkte im Indexbuffer stehen. So dass jeweils die Tangente vom Vertex mit der Nummer 1 übernommen würde. Oder kann man das für jedes Dreieck irgendwie einzeln aussuchen?
Wenn nicht, dann stelle ich mir die richtige Sortierung der Vertices ziemlich tricky vor. Schon bei deinem idealisierten Mesh tritt das Problem auf, dass einige Dreiecke im und andere gegen den Uhrzeigersinn angeordnet sind, was dann wiederum schlecht für Backface-Culling ist. [Edit: Wobei sich das in diesem Fall leicht fixen ließe.] Also ich glaube, man kommt nicht ohne zusätzliche Vertices aus. Über den Faktor, wieviel genau man mehr braucht, könnte man streiten bzw. forschen. Ich glaube, das spare ich mir aber und werde stattdessen den Geometryshader-Weg gehen, sobald ich die Zeit dazu finde. :wink: (Schon wieder Mitternacht! :shock: )

Es sei denn, jemand bringt hier noch ein wirklich gutes Argument für eine bessere Methode.

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jan 13, 2014 14:06 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Code:
  1. Ich nehme an, die Nummerierung in deinem Bild gibt für jedes Dreieck an, in welcher Reihenfolge seine Eckpunkte im Indexbuffer stehen.

Ja, genau.
Zitat:
So dass jeweils die Tangente vom Vertex mit der Nummer 1 übernommen würde.

Ja
Zitat:
Oder kann man das für jedes Dreieck irgendwie einzeln aussuchen?

Im Prinzip schon, weil man kann die Vertices ja einfach durch wechseln.

Zitat:
Wenn nicht, dann stelle ich mir die richtige Sortierung der Vertices ziemlich tricky vor.

Tricky ist es schon. Ich habe gestern ein wenig gegrübelt und denke, dass sich eine gute Sortierung relativ leicht finden lässt. Man fängt einfach an einer Ecke an, und legt hier einen Startvertex liegt. Dann fügt man einfach immer mehr Polygone, wobei man darauf achtet, dass der Startvertex wenn möglich möglichst nicht an der Front liegt, wo möglicherweise noch mehr Polygone "angeschlossen" werden müssen.

Zitat:
dass einige Dreiecke im und andere gegen den Uhrzeigersinn angeordnet sind, was dann wiederum schlecht für Backface-Culling ist.

Das stimmt in diesem Fall, weil ich vergessen habe, darauf zu achten. Allerdings kann man die Polygone ohne Einschränkungen immer im oder gegen den Uhrzeigersinn anordnen. Man kann die Vertexreihenfolge ja verdrehen, ohne das sich der Umlaufsinn ändert. Das wäre also keine Einschränkung.

Zitat:
Also ich glaube, man kommt nicht ohne zusätzliche Vertices aus.

Man kann sich theoretisch recht einfach überlegen, dass das solange problemlos funktioniert, wie nur maximal 3 Polygone ein Vertex verwenden. Ein Polygon hat bei uns immer drei 3 Ecken. Einer davon muss der Startvertex sein. Es darf also für ein Mesh ohne zusätzliche Vertices bei der Methode, nie mehr Dreiecke als Vertices geben. Unregelmäßigkeiten wie oben im Beispiel kann man mit Vertices ausgleichen, an denen weniger als 3 Dreiecke angeschlossen sein. (Siehe Mittelpunkt). Das heißt bei einer (unendlich) großen Fläche aus einen Mesh wie im Beispiel braucht man genau doppelt soviel Vertices. Das Problem kann man dadurch reduzieren, in dem man Quads verwendet. Mit garantierten Quads, heißt es dann maximal 4 Quads pro Vertex. Quads gibt es ja in OpenGL nicht mehr, aber man kann ja einfach zwei planare Dreiecke verwenden. Solange sie garantiert planar sind, sollten sie sich ja den Startvertex teilen können.

Zitat:
stattdessen den Geometryshader-Weg gehen

Ich denke auch, dass es die einfachere Lösung ist.
Außerdem spart man auch die Verarbeitung auf der CPU und viel Verwaltungsaufwand.
Solche Einschränkungen können außerdem die Verwendung von dynamischer Geometrie verhindern.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jan 13, 2014 17:58 
Offline
DGL Member
Benutzeravatar

Registriert: So Sep 26, 2010 12:54
Beiträge: 238
Wohnort: wieder in Berlin
Programmiersprache: Englisch
Ja als Vertexattribut. Aber wenn das im GS "keinen" Aufwand bedeutet, kann man ja mal beide Wege nebeneinanderhalten und mal prüfen was denn besser funzt, bei kleinen und großen Szenen. Hmm die Grafikkarte kennt übrigens auch nur Dreiecke, also macht es durchaus Sinn alles nur als Dreiecke zu rendern.


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 16 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Foren-Übersicht » Programmierung » OpenGL


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 12 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:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.046s | 19 Queries | GZIP : On ]