Die Diskontinuität der Texturkoordinaten mit Mipmaps erzeugt Nähte

9

Ich habe gerade angefangen, openGL zu lernen und bekomme dieses Artefakt, wenn ich eine Kugel mit Mipmaps texturiere. Wenn das Fragment den Rand meiner Textur abtastet, erkennt es die Diskontinuität (z. B. von 1 bis 0) und wählt die kleinste Mipmap aus, wodurch diese hässliche Naht entsteht:

hässliche Naht http://cdn.imghack.se/images/6bbe0ad0173cac003cd5dddc94bd43c7.png

Also habe ich versucht, die Verläufe manuell mit texturGrad zu überschreiben:

//fragVert is the original vertex from the vertex shader
vec2 ll = vec2((atan(fragVert.y, fragVert.x) / 3.1415926 + 1.0) * 0.5, (asin(fragVert.z) / 3.1415926 + 0.5));
vec2 ll2 = ll;
if (ll.x < 0.01 || ll.x > 0.99)
    ll2.x = .5;
vec4 surfaceColor = textureGrad(material.tex, ll, dFdx(ll2), dFdy(ll2));

Jetzt bekomme ich zwei Nähte statt einer. Wie kann ich sie loswerden? Und warum erzeugt der obige Code 2 Nähte?

2 eams http://cdn.imghack.se/images/44a38ef13cc2cdd801967c9223fdd2d3.png

Sie können es nicht an den beiden Bildern erkennen, aber die beiden Nähte befinden sich auf beiden Seiten der ursprünglichen Naht.

Omikun
quelle
2
Ist Ihre Kugel ein Netz oder verfolgen Sie die Kugel analytisch im Shader oder ähnlichem? Wenn es sich um ein Netz handelt, lösen die Leute dieses Problem normalerweise, indem sie die Eckpunkte entlang der Naht duplizieren, sodass die Verts auf einer Seite u = 0 und auf der anderen Seite u = 1 haben können.
Nathan Reed
Ich gehe davon aus, dass Ihr Code zwei Nähte generiert, weil er zwei Diskontinuitäten erzeugt - eine, bei der Sie von 0,01 auf 0,5 springen, und eine, bei der Sie von 0,99 auf 0,5 springen. Es ist das gleiche Problem wie wenn du von 0 auf 1 springst - nicht so groß wie ein Sprung, aber immer noch ein großer Sprung.
Nathan Reed
Ich habe ein Netz für die Kugel. Es ist ein Dekaeder, damit die Eckpunkte nicht an der Naht ausgerichtet sind. Das Lustige an den beiden Nähten ist, dass die ursprüngliche Naht weg ist. Außerdem bekomme ich die gleichen 2 Nähte, auch wenn ich sie auf 0,01 oder 0,99 klemme.
Omikun
Können Sie einen Screenshot des Falles mit zwei Nähten posten? Und wie erzeugen Sie die UVs (können Sie den relevanten Teil Ihres Codes veröffentlichen)? Ich nehme an, Sie generieren sie prozedural im Shader und interpolieren sie nicht nur aus den Eckpunkten, sonst würde Ihr Dodekaedernetz überhaupt nicht funktionieren.
Nathan Reed
Ich habe die Frage mit einem Bild der beiden Nähte aktualisiert. Sie befinden sich auf beiden Seiten der ursprünglichen Naht, aber die ursprüngliche Naht ist nicht vorhanden. Ich denke, die UV werden von den Eckpunkten interpoliert, nicht sicher. Der Code ist jetzt in der Frage.
Omikun

Antworten:

4

Basierend auf dem Shader-Code, den Sie veröffentlicht haben, interpolieren Sie nicht die UVs von den Scheitelpunkten, sondern es scheint, dass Sie die 3D-Position interpolieren (fragVert ) und berechnen dann die UVs durch Transformation in sphärische Koordinaten.

Ihre Analyse ist insofern korrekt, als die kleinste Mipmap ausgewählt wird, wenn eine Diskontinuität vorliegt, da die Auswahl der Mipmap auf Ableitungen basiert, die numerisch aus den in benachbarten Pixeln verwendeten UVs geschätzt werden. Wenn ein Pixel u = 0 und ein anderes u = 1 hat, erhalten Sie eine sehr große Ableitung. Ihr versuchter Fix hat das gleiche Problem, da große Ableitungen um u = 0,01 und u = 0,99 auftreten, weshalb zwei Nähte auf beiden Seiten der ursprünglichen Naht erscheinen.

Ein relativ einfacher Ansatz zur Behebung des Problems wäre die Entscheidung, welche Mip-Ebene Sie verwenden und anrufen möchten textureLod , um sie direkt zu testen. Wenn sich der Planet immer ziemlich nahe an der Kamera befindet, können Sie den Mip-Pegel einfach auf 0 fest codieren (oder die Mip-Pegel überhaupt nicht in die Textur aufnehmen). Andernfalls könnte es auf dem log2 der Entfernung des Punkts von der Kamera basieren, skaliert durch einige geeignete Faktoren. Beachten Sie, dass dadurch die anisotrope Filterung effektiv deaktiviert wird.

Ein "korrekterer" Ansatz wäre die Berechnung einiger Derivate höherer Qualität. Anstatt dFdxund dFdyauf den UVs zu verwenden, die aufgrund der Diskontinuitäten aufweisen atan2, können Sie dFdxund dFdyauf fragVert(die rund um die Kugel kontinuierlich sind) anwenden und dann einen Kalkül (Kettenregel) verwenden, um die Formel zu finden, um die UV-Derivate zu erhalten aus den Positionsderivaten. Dies ist komplizierter und langsamer, hat jedoch den Vorteil, dass die anisotrope Filterung funktionieren sollte.

Da Sie OpenGL noch nicht kennen, möchte ich nur darauf hinweisen, dass die Berechnung von UV-Strahlen aus sphärischen Koordinaten zwar eine absolut gültige Methode zur Texturierung einer Kugel darstellt, die meisten Menschen jedoch nicht die "übliche" Methode wählen. Es ist üblicher, ein Kugelnetz zu erstellen, bei dem UVs pro Scheitelpunkt angegeben und einfach vom Scheitelpunkt-Shader zum Pixel-Shader übergeben werden (linear über jedes Dreieck interpoliert). Scheitelpunkte werden entlang der Naht so platziert , dass zwei Kopien jedes Scheitelpunkts an genau denselben Positionen vorhanden sind, wobei die Hälfte mit u = 0 auf einer Seite mit den Dreiecken verbunden ist und die andere Hälfte mit u = 1 verbunden ist die Dreiecke auf der anderen Seite. Dies eliminiert jede sichtbare Naht und erfordert keine Tricks im Pixel-Shader.

Nathan Reed
quelle