Objekte Zeichnen, welche Teilweise hinter dem Betrachter liegen



  • Hallo,

    ich möchte eine 3D-Linie zeichnen, welche wie eine Bahn-Schiene aussieht, wo man drauf steht. D.h., die Linie läuft ganz weit nach vorne zum Horrizont, und hinter dem Betrachter geht die Linie auch noch lang.

    Mein Erwartetetes Ergebniss ist also eine 2D-Linie, welche als Startpunkt die 2D-Mitte des Bildschirms hat und als Endpunkt (X-Mitte vom Bildschirm; Y-Ganz unten vom Bildschirm).

    Erst probiere ich es in DirectX.

    Im Vertexshader errechne ich hiermit die Clip-Coordinaten:

    PS_IN VS_Standard(VS_IN input)
    {
    output.pos = mul(float4(input.pos.xyz, 1.0), WorldViewProj);
    }

    Im Pixelshader gebe ich es dann aus:

    float4 PS_Standard( PS_IN input ) : SV_Target
    {
    return float4(1,0,0,0);
    }

    So berechne ich die WorldViewProj-Matrix, um von Objektkoordinaten in Clippingkoordinaten zu rechnen:

    SlimDX.Matrix mv_obj_to_eye = m_modelviewMatrix * m_projMatrix;

    Die Projektionsmatrix berechne ich wie hier beschrieben:

    http://wiki.delphigl.com/index.php/gluPerspective

    Die Ausgabe klappt.

    Nun will ich es selber probieren.

    Ich gehe dabei wie hier beschrieben vor:

    http://www.songho.ca/opengl/gl_transform.html

    Vektor window = new Vektor(0, 0, 0);
    var matrix = Matrix.MatrixMul(projectionMatrix, modelviewMatrix);
    
    float[] ve = new float[] { obj.x, obj.y, obj.z, 1 };
    Matrix.VMatMult(matrix, ve); // umrechnen von Objektkoordinaten in Clip Koordinaten
    ve[0] /= ve[3];                                     // umrechnen von Clip Koordinaten in Normalisierte Clip Koordinaten
    ve[1] /= ve[3];                                     // x, y und z liegen nun im Bereich [-1 ; +1]
    ve[2] /= ve[3];                                     // wenn z im Bereich [0 ; 1], dann ist Punkt sichtbar
    
    float wideHalf = buffer.Length / 2;                 //umrechnen in Fensterkoordinaten
    float highHalf = buffer[0].Length / 2;
    window.x = wideHalf * (ve[0] + 1);
    window.y = buffer[0].Length - highHalf * (ve[1] + 1);
    window.z = (ve[2] + 1) / 2;
    

    Die X-Koordinate stimmt. Die Window.y-Koordinate stimmt nicht. Der Punkt welche ich als Input hier reingebe liegt HINTER dem Betrachter. Warum kommt OpenGL und DirectX damit klar und ich nicht. Bis zum Ausrechen der Clip-Koordinaten mach ich alles gleich. Nur bei der Perspektivischen Division und der Viewporttransformation bin ich mir nicht sicher, was OpenGL/DirectX dann wirklich macht.

    Wie kann ich nun also Linien zeichnen, wo der Startpunkt VOR dem Betrachter liegt und der Endpunkt HINTER dem Betrachter?



  • XMAMan schrieb:

    Die X-Koordinate stimmt. Die Window.y-Koordinate stimmt nicht. Der Punkt welche ich als Input hier reingebe liegt HINTER dem Betrachter. Warum kommt OpenGL und DirectX damit klar und ich nicht.

    Weil ein solcher Punkt nach der WorldView Transformation eine negative z Koordinate und damit nach der Projection Transformation eine negative w-Koordinate hat, was dazu führt, dass der Punkt durch die Division durch w auf falsche Koordinaten projiziert. Du musst die Linie gegen die near-Plane clippen, also den Bereich der hinter dem Betrachter liegt abschneiden, um das zu verhindern.



  • D.h., ich soll nach den Umrechnen von Objekt- in Augkoordinaten prüfen, ob der Z-Wert < z-Near ist. Wenn ja, dann mache Zuweisung: Z := zNear

    Ok, bei einer Linie muss ich also den Schnittpunkt zwischen der Linie und der Z-Near-Plane machen.

    Ist die Z-Near-Plane dann in Augkoordinaten definiert oder was?



  • Du kannst es im Prinzip in Augkoordinaten machen, ja, ist vermutlich die einfacher verständliche Variante; die Grafikkarte macht es in projektiven Koordinaten (daher auch der name "Clipspace" ;)).


  • Mod

    im Prinzip beantwortest du hier schon deine frage

    ve[2] /= ve[3];                                     // wenn z im Bereich [0 ; 1], dann ist Punkt sichtbar
    

    alles was <0 (und eigentlich > ve[3]) ist nicht sichtbar -> clip gegen beides.

    deine umrechnung in "fenster koordinaten" kannst du eigentlich auch als matrix aufbauen und zu den oberen matritzen dazu multiplizieren. Das clipping funzt dann weiterhin noch und es spart dir den code.

    [ w/2, 0, 0, -w/2,
      0, h/2, 0, -h/2,
      0,  1, 0, 0,
      0,  0, 0, 1}
    


  • Ich habe nun das Clipping in Homogenen Koordinatenraum wie hier beschrieben gemacht:

    http://fabiensanglard.net/polygon_codec/

    Ich musste diesen C++-Quelltext nach C# übersetzen und bin sehr froh, dass es in C# keine Zeigerarithmetik gibt.

    Hier ist mein aktueller Stand:

    http://fs1.directupload.net/images/150322/3oxli5du.jpg

    An der rechten Wand sieht man, dass es noch ein Fehler gibt. Trotzdem ist das ganze schonmal besser als vorher.

    Vielleicht hat ja jemand von euch noch eine Idee, wie ich im Debugger oder so vorgehen muss, um diesen Fehler zu finden.

    Eine Sache fällt mir an diesen Algorithmus noch auf:

    Dort steht ja folgende Zeile:

    if (previousDot * currentDot < 0)

    das bedeute ja, wenn ein Punkt vom Polygon im Sichtbereich ist, und der darauffolgende oder vorhergehende Punkt nicht, dann wird geclippt. Was passiert aber, wenn alle Punkte des Polygons außerhalb des Sichtbereichs sind man aber das Polygon trotzdem sieht: Beispiel: Ich stehe ganz nah vor einer Wand. Alle 4 Eckpunkte sind außerhalb des Bildschirms.

    Wie muss ich da vorgehen?


  • Mod

    XMAMan schrieb:

    Ich musste diesen C++-Quelltext nach C# übersetzen und bin sehr froh, dass es in C# keine Zeigerarithmetik gibt

    kann ich gut verstehen, wenn ich manchmal sourcen sehe, dann bluten mir die augen und dann denke ich dass man manchen leuten die meisten werkzeuge wegnehmen sollte und sie sich nur noch mit plastikloeffeln beschaeftigen duerfen sollten 😃

    Hier ist mein aktueller Stand:

    http://fs1.directupload.net/images/150322/3oxli5du.jpg

    schoen dass du immer screens postest, das schaut nach spass aus :). manche stellen (z.B. rand vom tisch oder stuhlbein) sehen noch aus als haettest du ein wenig was falsch.

    An der rechten Wand sieht man, dass es noch ein Fehler gibt. Trotzdem ist das ganze schonmal besser als vorher.

    manchmal verwechseln manche ob wie z oder w clippen bzw auf <0 testen. check deinen source, vielleicht ist das der fehler 😉

    Vielleicht hat ja jemand von euch noch eine Idee, wie ich im Debugger oder so vorgehen muss, um diesen Fehler zu finden.

    find das poly und step durch das clipping. wenn es ein 3d model ist das du laedst, loesch in deinem 3d editor einfach alle anderen triangles raus zu debug zwecken.

    Eine Sache fällt mir an diesen Algorithmus noch auf:

    Dort steht ja folgende Zeile:

    if (previousDot * currentDot < 0)

    das bedeute ja, wenn ein Punkt vom Polygon im Sichtbereich ist, und der darauffolgende oder vorhergehende Punkt nicht, dann wird geclippt. Was passiert aber, wenn alle Punkte des Polygons außerhalb des Sichtbereichs sind man aber das Polygon trotzdem sieht: Beispiel: Ich stehe ganz nah vor einer Wand. Alle 4 Eckpunkte sind außerhalb des Bildschirms.

    Wie muss ich da vorgehen?

    du clippst alle edges immer nur gegen ein seite/ebene und wenn dort alle punkte ausserhalb sind, dann schneidet nichts die ebene und ist somit raus.



  • Ich habe den Fehler gefunden.

    Er lag in dieser Zeile hier:

    intersectionFactor = (W_CLIPPING_PLANE - previousVertice.W) / (previousVertice.W - currentVertice.W);
    

    So muss aber korrekter Weise so lauten:

    intersectionFactor = (/*W_CLIPPING_PLANE -*/ previousVertice.W) / (previousVertice.W - currentVertice.W);
    

    Der Autor von dieser Seite hier

    http://fabiensanglard.net/polygon_codec/

    Hat nicht direkt an der w=0 (z-Near-Ebene) geklippt sondern er wollte an der

    w=0.00001-Ebene clippen, um eine Division durch 0 zu verhindern. Dummerweise hat er durch die oben genannte Zeile das Vorzeichen von previousVertice.W damit umgedreht. Hätte er stattdessen:

    previousVertice.W - W_CLIPPING_PLANE

    geschrieben, dann wäre das vermultlich auch OK gewesen.

    Seis drum. Hier ist nun das Ergebnis von zwei Testscenen.

    http://fs1.directupload.net/images/150323/39d3arza.jpg

    @Rapso: Du sagtest, ich soll mich mit Texturfiltern und Antialiasing auseinder setzen. So... die Szene mit den Linien ist meine Textur-Filter-Testszene.

    Ganz Rechts siehst du, wie DirectX11 Anisotroph filtert. Ich will jetzt Bilinear, Trilinear und ein Anisothropen Filter für mein CPU-Renderer und mein Raytracer implementieren. Deswegen musste vorher das Clipping gemacht werden, damit die Testszene überhaupt sichtbar ist.

    Warum ist die Textur ganz hinten bei DirectX immer noch falsch? Hat der Filter eine Grenze?

    So stelle ich ihn ein:

    SamplerState samAnisotropic
    {
    	Filter = ANISOTROPIC;
    	MaxAnisotropy = 4;
    
    	AddressU = WRAP;
    	AddressV = WRAP;
    };
    

    Ich habe es auch schon mit MaxAnisotropy = 16 probiert. Hat aber kein Unterschied gebracht.

    Eine Frage noch:

    Texturfilter machen die Textur schärfer
    Antialiasing macht die Kanten von 3D-Objekten weicher

    Hab ich das so richtig verstanden?

    Wenn ja, dann sind Texturfilter doch viel wichtiger. Kennst du ein Beispiel, wo man die Wichtigkeit von Antialiasing sieht?

    Das hier überzeugt mich nun nicht gerade:

    http://media.indiedb.com/images/games/1/7/6034/AAcomp.jpg

    Das Texturfilter-Beispiel find ich wiederum super hier:

    http://www.tweakguides.com/images/GGDSG_22.jpg



  • XMAMan schrieb:

    Warum ist die Textur ganz hinten bei DirectX immer noch falsch? Hat der Filter eine Grenze?

    Jap, dein Stichwort lautet Mip-Mapping... 😉



  • @dot
    Versteh ich nicht ganz.
    Die Mip-Maps gehen doch normalerweise runter bis auf 1x1 Pixel.
    Dann müsste doch hinten alles schön einheitlich grau sein.

    Oder meinst du dass das Problem ist dass hier eben keine Mip-Maps verwendet wurden?



  • Ich habe das Anisotrophe Filtern so verstanden, dass man einfach nur den Farbwert des Footprints

    http://de.wikipedia.org/wiki/Footprint_Assembly

    Möglichst genau berechnen muss. Um so mehr Taps (Rechtecke, die man da rein legt) man nimmt, um so genauer wird also integriert.

    Mip-Mapping ist lediglich ein Beschleunigungstrick, um N*N-Texel zu Addieren. Oder was will uns der Herr Dot hiermit sagen?



  • XMAMan schrieb:

    Möglichst genau berechnen muss. Um so mehr Taps (Rechtecke, die man da rein legt) man nimmt, um so genauer wird also integriert.

    Die Taps sind punktuelle Samples.
    Und wenn man punktuelle Samples mit regelmässigen Abständen aus einem regelmässigen Muster nimmt, dann bekommt man eben manchmal solche Artefakte.

    Klar, wenn du unendlich viele Taps hättest, dann bräuchtest du kein Mip-Mapping mehr.
    16 Taps sind da aber einfach viel zu wenig.


  • Mod

    XMAMan schrieb:

    Ich habe den Fehler gefunden.

    Er lag in dieser Zeile hier:

    intersectionFactor = (W_CLIPPING_PLANE - previousVertice.W) / (previousVertice.W - currentVertice.W);
    

    So muss aber korrekter Weise so lauten:

    intersectionFactor = (/*W_CLIPPING_PLANE -*/ previousVertice.W) / (previousVertice.W - currentVertice.W);
    

    rapso schrieb:

    manchmal verwechseln manche ob wie z oder w clippen bzw auf <0 testen. check deinen source, vielleicht ist das der fehler

    wie gedacht.

    Der Autor von dieser Seite hier

    http://fabiensanglard.net/polygon_codec/

    Hat nicht direkt an der w=0 (z-Near-Ebene) geklippt sondern er wollte an der

    w=0.00001-Ebene clippen, um eine Division durch 0 zu verhindern. Dummerweise hat er durch die oben genannte Zeile das Vorzeichen von previousVertice.W damit umgedreht. Hätte er stattdessen:

    previousVertice.W - W_CLIPPING_PLANE

    geschrieben, dann wäre das vermultlich auch OK gewesen.

    bitte nicht so.
    du musst z clippen. gegen w clippen ist unsinn.

    @Rapso: Du sagtest, ich soll mich mit Texturfiltern und Antialiasing auseinder setzen. So... die Szene mit den Linien ist meine Textur-Filter-Testszene.

    Ganz Rechts siehst du, wie DirectX11 Anisotroph filtert. Ich will jetzt Bilinear, Trilinear und ein Anisothropen Filter für mein CPU-Renderer und mein Raytracer implementieren. Deswegen musste vorher das Clipping gemacht werden, damit die Testszene überhaupt sichtbar ist.

    Warum ist die Textur ganz hinten bei DirectX immer noch falsch? Hat der Filter eine Grenze?

    ja, die grenze ist bei aktueller hardware meist 16 samples.

    wenn du also eine 256x256 texture auf 1x1 pixel zeichnest, dann wird die hardware nur 16:65536 -> 1:4096 samples machen. eindeutig nicht genug. in software kannst du das natuerlich besser machen und da du eh tausende samples/pixel fuer deinen tracer gemacht hattest, haettest du mit leichtem jittern das aliasing problem der texturen und von der geometrie zugleich loesen koennen.

    mipmaps sind an sich nur eine performance optimierung fuers anisotropische filtering (jedenfalls wurden sie damals aus diesem grund 'erfunden'). du kannst entweder 256x256 samples nehmen oder 1x1 aus der mipmap.

    SamplerState samAnisotropic
    {
    	Filter = ANISOTROPIC;
    	MaxAnisotropy = 4;
    
    	AddressU = WRAP;
    	AddressV = WRAP;
    };
    

    MaxAnisotropy ist das limit von dem wir sprechen.

    Ich habe es auch schon mit MaxAnisotropy = 16 probiert. Hat aber kein Unterschied gebracht.

    doch, hat es, aber es ist nicht die welt. du sieht lediglich dass die grenze wo noise anfaengt ein wenig nachhinten verschoben wird.

    du musst fuer echtzeit dinge mipmaps verwenden.

    Eine Frage noch:
    Texturfilter machen die Textur schärfer

    nicht wirklich, es kommt auf den filter an. bilinear und trilinear macht texturen eigentlich eher matschiger, aber gut angewendet vermeidest du halt texture aliasing und gerade bei bewegung siehst es dann besser aus als flimmern.
    es gibt einen mipmap bias mit dem du die selektion der mipmap einstellen kannst, dann hast du die moeglichkeit es pixeliger oder vermatschter zu machen.

    Antialiasing macht die Kanten von 3D-Objekten weicher

    weiche wuerde ich nicht sagen, eher "definierter". wenn du ein schachbrettmuster auf einem pixel hast, welche farbe sollte der pixel sein? schwarz? weiss? ... unter den meisten gesichtspunkten wuerde man eigentlich eine art grau erwarten.

    Wenn ja, dann sind Texturfilter doch viel wichtiger. Kennst du ein Beispiel, wo man die Wichtigkeit von Antialiasing sieht?

    Das hier überzeugt mich nun nicht gerade:

    http://media.indiedb.com/images/games/1/7/6034/AAcomp.jpg

    Das Texturfilter-Beispiel find ich wiederum super hier:

    http://www.tweakguides.com/images/GGDSG_22.jpg

    Es ist beides dasselbe.
    Was ist eine textur? es ist eine platz sparende art und weise geometrische details abzulegen.
    du kannst ein schachbrettmuster haben indem du schwarz-weisse quads nebeneinander anordnest, du kannst aber auch eine textur dafuer nutzen.
    machst du ein sample pro pixel (bzw samplest immer an derselben stelle pro pixel), wird sowohl die geometrische loesung wie auch die textur loesung das gleiche bild liefern. samplest du nun pro pixel viele male an verschiedenen stellen, werden beide loesungen ebenfalls dasselbe bild liefern.. ala
    http://http.developer.nvidia.com/GPUGems/elementLinks/fig25-01a.jpg

    wieviel antialiasing noetig ist haengt vom rauschen ab. dabei ist das rauschen fuer ein bild nicht sonderlich wichtig, viel wichtiger (weil viel mehr bemerkbar) ist das bei bewegung.

    bei pixar regeln das graphiker pro szene, manchmal sind es nur 5x5 samples, manchmal 33x33 samples.


Anmelden zum Antworten