Texturfilter selber schreiben - Warum ist Textur nicht perfekt scharf?
-
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?