[GLSL] Pixelungenauigkeit im Fragment Shader

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Benutzeravatar
Raphael
Beiträge: 65
Registriert: 22.12.2011, 13:39
Echter Name: Raphael Menges

[GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von Raphael »

Hi,

ich arbeite gerade an einer GUI-Lib für Eyetracking im Rahmen meiner HIWI-Anstellung (mehr zum EU-Projekt bei dem ich mitarbeite hier: http://www.mamem.eu/project/abstract/). Für das Rendering benutze ich OpenGL 3.3 Core und das klappt aktuell unter Linux und Windows sehr gut. Nun gibt es Buttons, welche Icons besitzen. Das Mesh eines solchen Buttons ist ein einfaches Quad. Das Icon und jegliche Interaktion wird über den Fragment Shader visualiert. Dieser ist dementsprechend etwas komplexer, funktioniert an sich aber wie erwartet. Es gibt aber je nach gerenderter Auflösung genau eine Pixelverschiebung an den Rändern des Icons, welches mittig auf den Button gerendert werden soll und daher mithilfe eines vec2 (iconUVScale) korrekt gezerrt wird. Damit wird gewährleistet, dass das Icon immer die korrekten Seitenrelationen hat, egal wie der Button aussieht:

Code: Alles auswählen

#version 330 core

out vec4 fragColor;
in vec2 uv;
uniform sampler2D icon;
uniform vec2 iconUVScale; // Correction
void main() {
   vec2 iconUV = uv - 0.5; // Move coordinates
   iconUV = vec2(iconUV.r * iconUVScale.x, iconUV.g * iconUVScale.y) + 0.5; // Scale coordinates and move back
   vec4 iconValue = texture2D(icon, iconUV).rgba; // Catching value of pixel
   float iconAlphaMask = clamp(floor(iconUV.r + 1), 0, 1) - clamp(floor(iconUV.r), 0, 1); // Horizontal mask
   iconAlphaMask *= clamp(floor(iconUV.g + 1), 0, 1) - clamp(floor(iconUV.g), 0, 1); // Vertical mask
   iconValue.a *= iconAlphaMask; // Mask to hide outer icon areas
...
Das ist nur ein Ausschnitt des Shaders. Im Endeffekt skaliere und verschiebe ich die Texturkoordianten so, dass das Icon in die Mitte des Buttons kommt. Dann mache ich eine Maske aus den Texturkoordinaten, sodass bei jenen mit Werten von 0 bis 1 eine 1 vorliegt und ansonsten 0 (iconAlphaMask). Die Maske wird dann in der letzten Zeile mit dem Alpha des Icons multipliziert damit dann um das Icon der Button sichtbar werden kann. Ich hab den Abschnitt mit dem Floor auch mal durch If und Else ersetzt um die Maske zu erzeugen und kam zum gleichen Problem. Der Effekt tritt sowohl unter Windows mit AMD als auf Intel unter Linux auf, sprich es liegt nicht am Treiber. Keine Ahnung, wie das Subtrahieren, Skalieren und Addieren sowas verursachen kann, weil der auf dem Screenshot gezeigte Fehler vom horizontalen Verlauf der Texturkoordinaten (uv.r) beeinflusst wird und dieser auf jeder vertikalen Höhe exakt gleich sein sollte.

Wahrscheinlich schwierig zu verstehen, hoffentlich hatte jemand schonmal ein vergleichbares Problem und eine Lösung!
Dateianhänge
Treppcheneffekt (Lena = Icon, Grau = Button, Schwarz = Hintergrund)
Treppcheneffekt (Lena = Icon, Grau = Button, Schwarz = Hintergrund)
Benutzeravatar
Sternmull
Establishment
Beiträge: 264
Registriert: 27.04.2007, 00:30
Echter Name: Til
Wohnort: Dresden

Re: [GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von Sternmull »

Hast du dir mal das Alpha der Textur anzeigen lassen? Möglicherweise ist das für die betroffenen Pixel ja bereits 0. Oder deine UV-Inputs sind nicht wie erwartet sondern für die unteren Punkte um einen Pixel nach rechts verschoben oder so. Ich würde da ganz stupide die verwendeten Werte einen nach dem anderen mal durch den Shader visualisieren lassen um zu sehen an welcher Stelle die unerwarteten Werte entstehen.
Das mit verschiedenen Treibern das gleiche passiert ist ja grundsätzlich erst mal eine gute Nachricht. Das heißt nämlich das das Problem höchstwahrscheinlich in deinem Code liegt und auch dort finden und beheben lässt.
Benutzeravatar
Raphael
Beiträge: 65
Registriert: 22.12.2011, 13:39
Echter Name: Raphael Menges

Re: [GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von Raphael »

Ok, ich bin wie du vorgeschlagen hast durch jede Zeile gegangen und jetzt zu dem Ergebnis gekommen, dass nichtmal die UVs gleichmäßig interpoliert werden. Unten ein Screenshot, der das verdeutlichen soll. Die eingebenen UVs sind nämlich entweder 0 oder 1 und sollten damit genau genug sein :cry: Die werden im Code definiert und nicht importiert oder so.
Dateianhänge
Minimalbeispiel
Minimalbeispiel
UVs.PNG (6.68 KiB) 2701 mal betrachtet
Benutzeravatar
Sternmull
Establishment
Beiträge: 264
Registriert: 27.04.2007, 00:30
Echter Name: Til
Wohnort: Dresden

Re: [GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von Sternmull »

Bis du dir wirklich ganz sicher das das Quad rechtwinklig und nicht transformiert ist? Wenn du dir ganz sicher bist das die UVs wie erwartet von 0 bis 1 gehen... dann kann das ja nur die Quelle der Verschiebung sein.
kristof
Beiträge: 92
Registriert: 19.01.2009, 13:05

Re: [GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von kristof »

Ich hab keine Idee woher das Problem kommen kann, wobei ich mir wie Sternmull auch schon sagte erstmal die Transformation des Quad noch einmal genau ansehen würde.
Vielleicht verstehe ich noch nicht ganz was du erreichen willst. Aber bist du dir sicher, dass es nicht einfacher (und vermutlich auch schneller) wäre einfach zwei Quads zu zeichnen. Eins für den Button und eins für das Icon. Klar, man hat etwas mehr Overdraw, dafür aber einen wesentlich simpleren Fragment Shader.
Benutzeravatar
Raphael
Beiträge: 65
Registriert: 22.12.2011, 13:39
Echter Name: Raphael Menges

Re: [GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von Raphael »

Wenn man es in einem Fragment Shader macht, bekommt man einige Effekt einfacher hin. Zum Beispiel möchte ich, dass während der Fokusierung durch das Auge eine Threshold über den Button hinweg visualisiert wird. Das ist einfach, wenn der ganze Button in einem Fragment Shader steckt und man nicht mehrere verschiedene ansprechen und dann noch die Transformation beachten muss. Unten ein Screenshot von allen wichtigen Stellen im Code, die draw()-Methode nennen wir mal Work in Progress. Bei manchen Zeilen könnte ich bestimmt noch was vor dem Dividieren zu Float casten :D Aber das sollte auf das spezielle Problem keinen Einfluss haben. Das Einfüllen der MeshData in Buffer übernimmt irgendwo mein AssetManager, aber da werden die UVs bestimmt nicht schief bei.
Dateianhänge
Shader.png
Benutzeravatar
xq
Establishment
Beiträge: 1589
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: [GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von xq »

Welches Texture Wrapping hast du? Bei "Wrap" ist nämlich sowohl 0.0 als auch 1.0 die gleiche farbe nämlich der mittelwert zwischen dem Linken und dem rechten Pixel. Deine Texturcoordinaten müssen von (1/width) bist 1-(1/width) gehen, damit du pixel perfect output bekommst
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Raphael
Beiträge: 65
Registriert: 22.12.2011, 13:39
Echter Name: Raphael Menges

Re: [GLSL] Pixelungenauigkeit im Fragment Shader

Beitrag von Raphael »

Gut zu wissen, ich hab beim Wrapping aber Clamp eingestellt. Im Moment habe ich es gelöst, indem ich die Zeilen mit dem Floor durch folgende ersetze:

Code: Alles auswählen

float iconAlphaMask = clamp(iconUV.r * 500, 0 , 1) - clamp((iconUV.r - 1) * 500, 0, 1); // Create horizontal mask
iconAlphaMask *= clamp(iconUV.g * 500, 0 , 1) - clamp((iconUV.g - 1) * 500, 0, 1); // Add vertical mask
Ich nutze hier den linearen Anstieg der Koordinaten und multipliziere ihn mit einem sehr hohen Wert, hier 500, um eine quasi Kante zu erhalten. Das sieht soweit in allen Auflösungen gut aus.
Antworten