Texturfilter selber schreiben - Warum ist Textur nicht perfekt scharf?
-
XMAMan schrieb:
rapso schrieb:
damit es korrekter ist, muestest du eigentlich im screenspace die samples generieren und dann auf die ebene projezieren, ansonstens ist dein sampling nicht perspektivisch korrekt.
Ich habe doch die 4 Eckpunkte des Pixels (vom Screenspace) auf die Textur projeziert. Innerhalb dieses Vierecks generiere ich die Zufallssamples.
Warum soll ich die Zufallspunkte lieber im Screenspace erzeugen und dann auf den Texturspace umrechnen? Damit die Verteilung der Zufallspunkte korrekt ist? Bei mir würde es dann zu einer schiefen Verteilung kommen, wenn die Textur schräg ist?
ja, damit es perspective correct ist. Durch deinen projeziertes viereck willst du ja integrieren was der pixel als farbe erhaelt und wenn du 50x50 groesser renderst und runterskalierst, hast du das korrekte filtering quasi simuliert und damit du an diese referenz kommst, musst du deinen filter perspective correct machen.
ist aber nur ein verbesserungsvorschlag gewesen, die hadware approximiert das auch sehr.rapso schrieb:
wenn du es super toll machen moechtest, muestest du natuerlich die texel zurechtschneiden anhand des projezierten schiefen vierecks.
Das verstehe ich nun garnicht. Ich generiere doch Punkte für die Abtastung. Egal, ob ich die Punkte nun im Texturspace oder Screenspace generiere. Wenn ich den Farb-Mittelwert von vielen Punkten bilde, wo muss ich da beim Texelauslesen noch was zurechtschneiden? Wenn dieses schiefe Viereck zu 80% auf Texel 1 liegt, und zu 20% auf Texel zwei, dann sorgt doch meine Zufallspunktabtastung schon dafür, dass 80% der Punkte auf Texel 1 liegen und 20% der Punkte auf Texel zwei. Der Mittelwert aller Punkte ist dann also Texel 1 * 0.8 + Texel 2 * 0.2. Wo ist das falsch?
das war nicht als zusatz, sondern als alternative. zZ machst du halt eine art monte carlo integration, ist wie dein path tracing. du siehst etwas mit 1sample, mit 4 samples ist die qualitaet der schaetzung schon doppelt so gut, mit 16 nochmal ... und damit es wirklich perfekt ist musst du unendlich samples machen, sonst kann es immer mal sein dass du rauschen siehst.
wuerdest du nun das projezierte viereck nutzen um dir die texturflaeche auszuschneiden, koenntest du immer ganz genau ausrechnen was das integral ist.
war nur ein vorschlag, du musst selbst entscheiden in welche richtung du damit gehst
(oh, und natuerlich muestest du die ausgeschnittene flaeche auf den pixel zurueckprojezieren und davon die flaechen als gewichtungen nehmen damit es perspektive correct ist
-
otze schrieb:
//eit nebenbei macht mir bim CPU-Renderer in der oberen Reihe dieses komische "Dreieck" etwas sorgen. Sind das polygongrenzen oder warum ist da alles etwas verschoben?
schaut aus wie clipping+ungenaues rasterisieren (man sieht scheinbar den hintergrund durch am rand manchmal)
das filtern beim Parallax mapping in software scheint aus zu jittern (koennte derselbe grund sein).
-
rapso schrieb:
otze schrieb:
//eit nebenbei macht mir bim CPU-Renderer in der oberen Reihe dieses komische "Dreieck" etwas sorgen. Sind das polygongrenzen oder warum ist da alles etwas verschoben?
schaut aus wie clipping+ungenaues rasterisieren (man sieht scheinbar den hintergrund durch am rand manchmal)
das filtern beim Parallax mapping in software scheint aus zu jittern (koennte derselbe grund sein).
Ich selbst vermute, es liegt an der Art und Weise, wie ich die UV-Koordianten über das Dreieck interpoliere.
Ich beschreibe mal wie ich das mache und ihr müsste mir sagen, ob das so richtig ist^^
Also ich habe eine Funktion DrawTriangle, welche ein Dreieck mit 3 Vertices bekommt. Jeder Vertex liegt in Objektkoordinaten(float-Zahl) vor. Ich rechne diese in Fensterkoordinaten um(auch noch float) und habe dann also ein Dreieck, dessen Eckpunkte float-Zahlen sind und im Screenspace liegt.
Nun mache ich für jeden der Eckpunkte eine Integer-Rundung und habe ein Dreieck, wo ich für jeden der Eckpunkte den Pixel weiß, wo es liegt.
Nun Iteriere ich Zeilen mit einer For-Int-I-Schleife von ganz linken Pixeln zu ganz rechten Pixel.
for (int x=XStart;x<=XEnd;x++) { float f = (x - XStart) / (float)(XEnd - XStart + 1); //f geht von 0 bis 0.99 Vektor tex1 = P1.TextcoordVektor / P1.position.z * (1 - f) + P2.TextcoordVektor / P2.position.z * f; //Textcoord interpoliert zwischen den Eckpunkten P1 und P2 for (int y=YStart;y<YEnd;y++) { float fy = (y- YStart) / (float)(YEnd - YStart + 1); Vektor tex = tex1 * (1 - fy) + tex2 * f; //Textcoord interpoliert zwischen den Eckpunkten tex1 und tex2 (tex1 und tex2 wurden in der x-Schleife berechnet. Sie liegen auf den Dreieckskanten) tex /= tex.z; //Dividiere durch das interpolierte 1 / z -> Siehe hier warum: http://en.wikipedia.org/wiki/Texture_mapping#Perspective_correctness //Lese Texel an der Stelle tex.x und tex.y Texture.GetPixel((int)tex.x, tex.y) -> Farbe für Pixel } }
Ganz am Anfang runde ich also die 3 Dreieckseckpunkte auf Integer. Ist hier der Fehler oder was könnte der Grund für das falsche Texturmapping sein?
Wenn ich das Clipping ausschalte, dann sieht das Ergebnis gleich aus. Es liegt also nicht am Clipping
-
for (int x=XStart;x<=XEnd;x++)
wieso <= ?
was passiert wenn du zwei transparente polys hast die eine kante teilen, zeichnest du die kante dann zweimal?float f = (x - XStart) / (float)(XEnd - XStart + 1);
woher kommt das +1 ?
-
Der Grund warum x/y bis <= anstatt nur < läuft ist folgender:
Stell dir mal vor die willst auf ein karrierten Blatt Papier eine Linie, welche von Links nach rechts läuft (Perfekt Horizontal) zeichnen. Die Linie soll 3 Kästchen lang sein. Die Kästchen haben einen Index. Z.B:
7,8,9
Wenn deine Linie auf den Kästchen 7,8,9 liegt, dann muss dein for-x von 7,8,9 laufen, also for (x=7;x<=9;x++)
Um die Länge der Linie in Pixeln zu berechen rechne ich also 9 - 7 + 1.
9 - 7 wäre ja nur 2. Die Linie ist doch aber 3 Kästen lang, wenn sie von 7 bis 9 läuft.
Ich zeichne also bei Polygonkonten die Nachbarkanten doppelt. Es kommt zum Z-Puffer-Fieght.
-
das ist nicht ganz optimal, durch dieses cheaten kannst du nie eine linie der laenge 0 haben. objekte die ganz weit entfernt sind und zu klein zum zeichnen waeren zeichnen bei dir pro vertex mehrere pixel.
das ist natuerlich eine persoenliche entscheidung des architekten des rasterizers (soweit ich weiss rasterisiert sogar renderman loecher, zumindestens in den uralten versionen), aber vielleicht magst du dir die DirectX rasterization rules anschauen
edit: link kaputtzerlegt vom forum
-
Ich habe das jetzt so geändert, dass ich nur von x < xend laufe. Die Kanten von benachbarten Pixeln werden jetzt nur noch einmal gezeichnet.
Diese Kante, welche Otze bemängelt hat ist nun weg.
Ich habe aber noch ein anders Problem(das schon vorher bestand).
So wie ich das hier beschrieben habe, so berechne ich doch die UV-Koordinate von der linken oberen Ecke des Pixels richtig?
Um nun die UV-Koordinaten des rechten oberen Pixels zu berechnen habe ich mir überlegt, dass +1 an diesen Stellen rechnen muss:
for (int x=XStart;x<=XEnd;x++) { float f = (x - XStart /*Obere Rechte Ecke des Pixels*/+ 1) / (float)(XEnd - XStart + 1); //f geht von 0 bis 0.99 Vektor tex1 = P1.TextcoordVektor / P1.position.z * (1 - f) + P2.TextcoordVektor / P2.position.z * f; //Textcoord interpoliert zwischen den Eckpunkten P1 und P2 for (int y=YStart;y<YEnd;y++) { float fy = (y- YStart /*Hier kein +1 da ich bereits bei oberer Pixelkante bin*/) / (float)(YEnd - YStart + 1); Vektor tex = tex1 * (1 - fy) + tex2 * f; //Textcoord interpoliert zwischen den Eckpunkten tex1 und tex2 (tex1 und tex2 wurden in der x-Schleife berechnet. Sie liegen auf den Dreieckskanten) tex /= tex.z; //Dividiere durch das interpolierte 1 / z -> Siehe hier warum: http://en.wikipedia.org/wiki/Texture_mapping#Perspective_correctness //Lese Texel an der Stelle tex.x und tex.y Texture.GetPixel((int)tex.x, tex.y) -> Farbe für Pixel } }
Diese Vorgehen hat aber einen Fehler. Die Textur hat dadurch am linken Bildschirmrand eine leichte Verzehrung. Woran könnte das liegen?
-
ich hatte dich gefragt gehabt wozu das +1 und jetzt hast du noch eines hinzugefuegt
die berechnung macht nicht so ganz sinn, was ist denn die 'linkeste' position die du erreichen kannst? die ist 1.
z.b. 3 pixel breite linie
float f = (x - XStart /*Obere Rechte Ecke des Pixels*/+ 1) / (float)(XEnd - XStart + 1); //f geht von 0 bis 0.99 x=0 -> (0 - 0+1)/(3-0+1) -> 1/2 -> 0.25 x=1 -> (1 - 0+1)/(3-0+1) -> 1/2 -> 0.5 x=2 -> (2 - 0+1)/(3-0+1) -> 1/2 -> 0.75
dabei moechtest du doch am linken rand 0 haben.
(x-XStartUngerundet)/(XEndUngerundet-XStartUngerundet);
manchmal ist das einfachste das mit den besten resultaten
-
Das war hier gerade ein Missverständniss. Meine Formel, um die LINKE OBERE Ecke zu berechnen geht so:
for (int x=XStart;x<XEnd;x++) { float f = (x - XStart) / (float)(XEnd - XStart);
Die Formel für obere rechte Ecke:
for (int x=XStart;x<XEnd;x++) { float f = (x - XStart + 1) / (float)(XEnd - XStart);
Zahlbeispiel für Linie, welche von x = 0 bis x = 2 geht
Linke Ecke:
x=0 -> (0 - 0)/(3-0) -> 0/3 -> 0 x=1 -> (1 - 0)/(3-0) -> 1/3 -> 0.33 x=2 -> (2 - 0)/(3-0) -> 2/3 -> 0.66
Rechte Ecke:
x=0 -> (0 - 0 + 1)/(3-0) -> 1/3 -> 0.33 x=1 -> (1 - 0 + 1)/(3-0) -> 2/3 -> 0.66 x=2 -> (2 - 0 + 1)/(3-0) -> 3/3 -> 1.00
Ich habe das +1 vergessen zu entfernen an dieser Stelle bei meinen letzten Beitrag:
(float)(XEnd - XStart + 1);
Im Quellcode steht aber jetzt
(float)(XEnd - XStart);
So. Die Frage, was an meiner Berechnung des rechten oberen Pixels falsch sein könnte bleibt bestehen. Wenn du willst, poste ich auch ein Beispielbild, wo ich die rechte obere Pixelecke zum Texturauslesen nehme, damit du den Fehler siehst.
-
XMAMan schrieb:
Das war hier gerade ein Missverständniss. Meine Formel, um die LINKE OBERE Ecke zu berechnen geht so:..
Die Formel für obere rechte Ecke:
sorry, ich bin verwirrt. ecken von was? ich dachte wir sprachen hier ueber die textur koordinate die du uebers dreieck bzw dessen scanline interpolierst.
ein beispielbild hilft mir jetzt vielleicht tatsaechlich
-
Ich meine die Ecke vom Pixel. Wenn ich über eine reihe von Pixeln laufe, dann muss ich ja entscheiden, an welcher Stelle vom Pixel ich die UV-Koordinate will. Will ich pro Pixel nur eine UV-Koordinate, dann reicht wohl Pixelmitte oder linke obere Pixelecke. Will ich aber das ganze Pixel(Nicht nur ein einzelnen Punkt aus dem Pixel) in den Texturspace projezieren, dann brauche ich wohl alle 4 Pixelecken (also deren UV-Koordianten)
-
kannst du ein wireframe vom boden zeigen?
ist das richtig dass du bei tex1 und tex2 von P1 interpolierst? sollte das nicht eher sowas sein wie:
Vektor tex1 = P1.TextcoordVektor/P1.positionz*(1.f)+P2.TextCoordVector/P2Position.z; Vektor tex2 = P3.TextcoordVektor/P1.positionz*(1.f)+P4.TextCoordVector/P2Position.z;
?
-
Ich habe den Fehler eingrenzen können. Er liegt beim Clipping in der Vertex-Interpolate-Funktion. Wenn ich das Clipping ausschalte, dann ist der Fehler weg.
Wenn ich ein Dreieck habe, wo P1 im Bildschirm liegt, und P2 draußen. Abstand zwischen P1 und P2 ist 10. Die Bildschirmkante liegt bei 5. Dann rufe ich LinearInterpolate mit f = 0.5 auf.
Die UV-Koordinaten werden hier ohne perspektivische Division interpoliert.
//f geht von 0 bis 1 private static Vertex LinearInterpolate(Vertex v1, Vertex v2, float f) { return new Vertex(v1.pos * (1 - f) + v2.pos * f, v1.normale * (1 - f) + v2.normale * f, v1.tTangent * (1 - f) + v2.tTangent * f, v1.texcoordU * (1 - f) + v2.texcoordU * f, v1.texcoordV * (1 - f) + v2.texcoordV * f); }
Das funktioniert also offentsichtlich nicht. Also probiere ich es nun so:
//f geht von 0 bis 1 private static Vertex LinearInterpolate(Vertex v1, Vertex v2, float f) { float posZ = 1 / v1.pos.z * (1 - f) + 1 / v2.pos.z * f; Vektor tex = v1.TextcoordVektor / v1.pos.z * (1 - f) + v2.TextcoordVektor / v2.pos.z * f; tex /= posZ; return new Vertex(v1.pos * (1 - f) + v2.pos * f, v1.normale * (1 - f) + v2.normale * f, v1.tTangent * (1 - f) + v2.tTangent * f, tex.x, tex.y); }
Das funkioniert aber auch nicht. Die Texturkoordinaten sind immer noch falsch. Woran liegt das? Wie muss ichs machen?