Shadow Volumes mit beliebiger Geometrie



  • Hi,
    ich versuche mich grade an Schatten à la Doom³.
    Dort wird der komplette Leval ja dynamisch beleuchtet und jedes Objekt kann auch einen Schatten werfen.

    Ich mache es im Mometn wie folgt:

    Die ShadowVolumes erstelle ich in einem VertexShader, durch den ich einfach die ganze Geometrie durchschicke.
    Wenn der Normalenvektor des Vertex vom Licht wegzeigt wird das Vertex vom Licht weggeschoben. Ansonsten bleibt es, wo es ist.
    Der Ablauf im Programm ist dann so:

    //zuerst rendere ich das ganze bild in den z-buffer, der ist also gefüllt.
    RenderZ()
    
    // allgemeine einstellungen:
    SetRenderState( D3DRS_ZWRITEENABLE,  FALSE );
    SetRenderState( D3DRS_STENCILENABLE, TRUE );
    SetRenderState( D3DRS_STENCILFUNC,   D3DCMP_ALWAYS );
    SetRenderState( D3DRS_STENCILPASS,   D3DSTENCILOP_KEEP );
    SetRenderState( D3DRS_STENCILFAIL,   D3DSTENCILOP_KEEP );
    SetRenderState( D3DRS_STENCILREF,    0x1 );  
    Clear( Stencilwert = 1 );
    
    // Shadow-Volumes rendern, BACKFACE
    SetRenderState( D3DRS_CULLMODE,  D3DCULL_CCW);
    SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_DECRSAT );
    Render()
    
    // Shadow-Volumes rendern, FRONTFACE
    SetRenderState( D3DRS_CULLMODE,  D3DCULL_CW);
    SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_INCRSAT );
    Render()
    
    // jetzt die Farben rendern wo der StencilBuffer > 0 ist.
    SetRenderState( D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL );
    Render()
    

    Ich hab mal einen Screen vom Ergebnis gemacht:
    ----> Bild <-----

    Wie man sieht, gibt es noch einige Fehler.
    - Am Boden fehlt ein Stück vom Schatten der Kugel.
    - An den Polen der Kugel gibt es je ein Dreieck, das nicht so richtig will.
    - Der Raum selbst wirft keinen Schatten (sieht man unten am Rand, wo der Boden so ne Stufe hat aber keinen Schatten wirft ).
    - Die Kugel beschattet sich nicht selbst.

    Die Frage ist jetzt, ob das überhaupt so gehen kann. Ich hab mal gelesen, dass die ShadowVolumes irgendwie geschlossen sein müssen, dass sie funktionieren.
    Ich wieß selbst nicht so genau, was ich eigentlich wissen will. Ist alles etwas verwirrend.
    Vielleicht hat von Euch jemand Erfahrung damit und kann sagen, wie man sowas wie in Doom3 macht ???



  • OK, nochmal einfacher gefragt:

    Ich brauche Shadow-Volumes von beliebigen Models. Da das anscheinend nicht geht, muss ich sie vorher manipulieren. Aber wie?

    Eine Möglichkeit könnte ich mir denken, die wäre aber recht lahm:

    Man erstellt einen zweiten Vertexbuffer. Für jedes Dreieck der Orginalgeometrie fügt man in diesen nun ein Prisma mit der Höhe 0 ein. Also 2 Dreiecke, die gegeneinander liegen und an allen 3 Seiten verbunden sind.
    Im Vertexshader würde das dann zu einem prismaähnlichen Volumen auseindandergezogen werden. Dann hätte man praktisch ein Shadow-Volume für jedes Polygon.
    Das könnte klappen, wäre aber wohl schweinelangsam.

    Hat denn niemand eine Idee?
    Ansonsten könnte ich versuchen, Carmack selbst mal eine mail zu schreiben und zu fragen. Ich fürchte aber, da kommt nicht das zurück, was ich will 😉



  • falls du genügend Kleingeld hast könntest du dir die Doom 3 Engine lizensieren lassen
    zwar gibts die noch nicht im Lizenzierungsprogramm von ID, aber müsste wohl bald drin sein(Quake 3 Engine kostet nur 250,000$; ich schätze mal das die Doom 3 Engine so um eine Million $ kostet


  • Mod

    du kannst für jede "kante" (also die zwei dreiecke) die du generierst noch die zwei normalen abspeichern von den dreiecken die sich die kante teilen, im vertexshader könntest du mittels der normalen und der richtung der zur lichtquelle (von dem vertex aus) prüfen ob beide normalen abgewand oder zu lichtquelle zugewand sind. falls sie verschieden sind, dann extrudierst du die kante, falls nicht, dann ist das keine siluetten-kante von der lichtquelle aus gesehen.

    das würde aber nur bei objekten wie z.B. der kugel funktionieren, weil du ja nicht weißt ob eine kante front oder backfaced ist.
    ansonsten muss das alles per cpu gemacht werden (ist dementsprechend langsam), es gibt gute paper auf der nvidia seite.

    rapso->greets();



  • 0x00000001 schrieb:

    - Die Kugel beschattet sich nicht selbst.

    Selfshadowing für eine Kugel ist doch unsinnig, das sie konvex ist?



  • ich kenn mich mit schatten nicht gut aus ... reicht wirklich das bisschen quellcode um schatten ala Doom 3 zu erstellen???!!!


  • Mod

    nein, reicht nicht, zumal niemand wirklich weiß wie doom3 das macht, das basiswissen haben viele, aber es stecken sicherlich unmengen an optimierungen drinne.
    das was da ist ist ein teil vom setup zum rendern, das rendern selbst ist da nicht drinne.

    rapso->greets();



  • welche für schritte bracht man, denn um shadow volumes von irgendwelchen objekten zu rendern?


  • Mod

    man muss die kanten herausfinden die das shadowvolume generieren und dann muss man von den kanten, entsprechen dem shadowvolume, die geometrie generieren. danach werden die vorderseiten gezeichnet und im stencil-buffer für jeden gesetzten pixel der wert um 1 erhöcht. danach werden die backfaces gezeichnet und für jeden pixel der wert im stencilbuffer um 1 erniedrigt. an den stellen, an denen ein pixel nicht wieder den ausganzswert hat, wurde eine vorderseite gezeichnet ohne passende rückseite, weil dazwischen ein objekt ist, dass den schatten "fängt". am ende wird nun die scene mit der lichtquelle beleuchtet, aber nur an den stellen an denen es keinen schatten gibt.

    naja, das so grob gesehen.

    rapso->greets();



  • Hört sich ziemlich langsam an, vorallem wenn man mehrere Lichtquellen verwenden will 😃

    Wie findet man, denn die Kanten von Objekten heraus? Bei einem Würfel dürfte es ja nicht so schwer sein, aber wie macht man das bei komplexen Objekten, und dann erst mit einem ganz "Raum"?


  • Mod

    ja das ist langsam, dabei ist das langsammste, dass man den vertexbuffer immer neu zur grafikkarten schicken muss und da die am besten laufen, wenn sie asynchron laufen dürfen und man durch den lock des buffers das verhindert, ist das wohl das langsammste dabei ... die menge der vertexdaten könnte aber auch zuviel für den bus sein.

    volumeshadows haben eben viele nachteile auch wenn deren theorie um so einfacher ist.

    wenn ich dir nun jede frage beantworte, wäre das ein tutorial für sich 😉 und davon gibt es schon genug.

    schau bei www.nvidia.com in der developer sektion nach papern, da bekommste auch nen source für z.B. shadowvolumes an quake models.

    rapso->greetS();



  • okay, danke!!!



  • TGGC schrieb:

    Selfshadowing für eine Kugel ist doch unsinnig, das sie konvex ist?

    Ja schon, aber es ist ja nicht immer eine Kugel. Aber auch die Kugel sollte sich beschatten, weil ich habe reflektierende Flächen, und in ungünstigen Winkeln reflektieren die auch auf der Schattenseite. Das wäre aber ausgeschlossen, wenn diese Bereiche auch im StencilSchatten wären.

    rapso schrieb:

    du kannst für jede "kante" (also die zwei dreiecke) die du generierst noch die zwei normalen abspeichern von den dreiecken die sich die kante teilen, im vertexshader könntest du mittels der normalen und der richtung der zur lichtquelle (von dem vertex aus) prüfen ob beide normalen abgewand oder zu lichtquelle zugewand sind. falls sie verschieden sind, dann extrudierst du die kante, falls nicht, dann ist das keine siluetten-kante von der lichtquelle aus gesehen.

    das würde aber nur bei objekten wie z.B. der kugel funktionieren, weil du ja nicht weißt ob eine kante front oder backfaced ist.
    ansonsten muss das alles per cpu gemacht werden (ist dementsprechend langsam), es gibt gute paper auf der nvidia seite.
    rapso->greets();

    Hm ich will das aber lieber nicht mit der CPU machen. Der Vertexshader ist so schön schnell. Und außerdem schreckt mich das 'Kantenfinden' ab. Ich dachte mir, dass man die Modelle einfach in die Länge ziehen kann und gut ist.
    Beim Beispiel der Kugel würde sich die eine Hälfte der Kugel dann vom Licht entfernen, und die andere bliebe da, wo sie ist. Die Polygone, die in der nähe der Kante liegen, weüden dann lang gezogen.
    Gut, so funktioniert das auch, aber eben nicht bei offenen Körpern.

    ... ich könnte die Räume so modellieren, dass die Wände immer eine Rückseite und Seiten haben. So wie man sie in echt auch kennt 😉 . Die zusätlichen Polygone würden dann beim Farbenrendern durchs Backface-Culling durchfallen oder wären nur sehr schmal.

    Ja ich glaube, das wird das beste sein. Ich kann nicht so gut programmieren dass ich die notwendigen Polygone automatisch erzeugen könnte. Manuell kann ich optimieren, aber automatisch könnte ich es nur so machen, wie ich oben schonmal geschrieben habe (also jedes Polygon in einen extra Körper verwandeln). Und da kämen deutlich mehr Polygone bei raus.

    Schon ärgerlich wenn man als Programmierer an so einem Problem scheitert, aber das Info-Studium hab ich noch vor mir. Hoffentlich bin ich danach schlauer als jetzt 🙄

    Danke, rapso und die anderen, für Eure Hilfe.

    P.S: Immer mehr wird mir bei meinem lächerlichen Versuch hier klar, dass Carmack wirklich der Gott sein muss 😮 Nicht nur, dass er es implementieren kann ;), nein, er kann es sogar noch bis in die Perfektion optimieren.


  • Mod

    so ganz kann ich dir net folgen.

    du hast zwar manchmal backfaces und manchmal frontfaces, aber die vertices können von einem front und einem backface gleichzeitig benutzt werden. ich würde mich also fragen wie du das extrudieren möchtest?

    carmack bekommt es deswegen so gut hin, weil er nichts anderes macht (und zu tun hat) als coden. was ihn ausmacht ist nicht genialität, sondern sehr viel fleiss und die daraus resultierende erfahrung, ich finde, dass das sehr viel mehr anerkennung verdient.

    da die cpu-graka-auslastung bei seiner doom3-alpha sehr hoch in richtung cpu ging, wird es wohl auch mit der cpu seine volumes generieren. als optimierung setzt er eine ebene höcher an. z.B. prüft er erst ob ein objet von einer lichtquelle aus überhaupt zu sehen ist, dazu nutzt er portale.
    bei den doom3 videos kann man sehen dass die meißte zeit nur eine lichtquelle einen schatten wirft, der rest beleuchtet, aber diese lichtquellen scheinen keine schatten werfen zu lassen.
    für statische lichtquellen kann man die volumes vorberechnen.
    optimieren kann man indem man z.B. mit der cpu die front und backfaces eines objektes bestimmt und sich das innerhalb eines bitfields speichert und mit dem bitfield für das objekt vom vorframe vergleicht, falls sich nix geändert hat, muss man das volume nicht neu generieren, weil die kanten gleich sind, man muss lediglich die transformation mittels vertexshader neu durchrechnen.

    das generieren der volumes kann man in zwei durchläufen generieren (also zwei durchgänge des meshes), die zeit ist also linear bei steigender polygonmenge.

    rapso->greets();



  • [qote]du hast zwar manchmal backfaces und manchmal frontfaces, aber die vertices können von einem front und einem backface gleichzeitig benutzt werden. ich würde mich also fragen wie du das extrudieren möchtest?"[/quote]

    Ja das stimmt, deßhalb werde ich wahrscheinlich alles aus Blöcken bauen müssen. Eine glatte Wand ist dann zB eine einfache Box. Man kann die Rückseite der Box dann zwar nie sehen, sie ist aber wichtig für die Shadow-Volumes. Außerdem kann ich immernoch normale, einseitige Wände bauen, wenn ich genau weiß dass die sowieso nie einen Schatten auf andere Sachen werfen kann.

    Bei den statischen Lichtquellen: Wäre es da besser, die Volumes dann am Anfang vorzuberechnen und in einen extra Vertexbuffer zu schreiben oder doch alles im VertexShader machen? Der Nachteil an ersterem wäre halt höherer Speicherverbrauch und man müsste die Vertexbuffer wechseln. Zweiteres käme meiner Faulheit sehr willkommen 😉 KA, ob der Shader sooo viel langsamer ist als die Standardvertexpipeline. Und da ich schon Dot3Bumpmapping eingebaut habe, werde ich wohl auch nicht so viele Polygone brauchen (ähnlich Doom3)

    Dass immer nur ein Licht in Doom3 Schatten wirft stimmt nicht ganz:

    http://doom3maps.ngz-network.de/images/news/reicht1.jpg

    Wie das mit den 'richtigen' Wänden funktioniert würde ich gern ausprobieren, muss jetzt aber erstmal formatieren 😋

    Ich meld mich dann nochmal, falls Windows mitmacht.



  • rapso schrieb:

    was ihn ausmacht ist nicht genialität, sondern sehr viel fleiss und die daraus resultierende erfahrung

    ...und Genialität! 🤡 👍



  • Was er einprogrammiert gibt es schon lange, hat nur niemand in Spieleengines eingebaut und das auch noch in der Kombination.



  • Sicher. "Akalabeth" hatte ja quasi auch schon 'ne 3D-Engine, vor Wolfenstein 3D etc. 😃

    Aber er bringt es in ph477er Quali in akzeptabler Geschwindigkeit auf HomeUser-Möhren zum Laufen. Das ist die Kunst!

    Und wenn man bald 3DMax-Renderbildchen oder PovRay-likes, an denen eure Möhren z.Z. noch Stunden rechnen, in ähnlicher Quali und in Echtzeit auf handelsüblichen Systemen bewundern kann... ➡ Er wird der Erste sein! 😃 😉 😋


  • Mod

    wobei wird er der erste sein?

    bumpmapping wie "expandeble" zu haben? schatten mit stencilbuffern wie schon fussballsimulationen? wüste gerne was du meinst.

    vielleicht sind ja auch die grafiker das, was deren hype ausmacht, ziemlich gut was die aus den bescheidenen möglichkeiten zaubern, die sie haben, mit max2 lichtquellen jedes objekt auszuleuchten und zumeinst nur eine im raum die schatten wirft...

    rapso->greets();



  • Hi,

    Hm ich will das aber lieber nicht mit der CPU machen. Der Vertexshader ist so schön schnell. Und außerdem schreckt mich das 'Kantenfinden' ab. Ich dachte mir, dass man die Modelle einfach in die Länge ziehen kann und gut ist.

    man sollte beachten, dass Shader==schnell nicht automatisch true ergibt. Es kommt darauf an, was Deine GPU und CPU sonst noch zu tun haben. 😉

    Das einfache Ausziehen des Modells in einem Shader funktioniert nur für sehr runde, weiche Modelle halbwegs und setzt voraus, dass die Vertices für mehrere Triangles verwendet werden. Sobald man für angrenzende Triangles auf derselben Position zwei Vertices verwendet oder sehr kantige Modele hat geht das so nicht in einem Shader.

    Wenn man auf der CPU "lästig" Kanten suchen muss, dann muss man für einen Shader eine andere lästige Setup Arbeit machen. Nämlich das einfügen degenerierter Quads. An jeder Kante des Modells ein zu einer Linie degeniriertes Viereck einfügen. Was das für den Vertex-Count des Modells bedeutet dürfte klar sein. Im Shader kann man dann tatsächlich einfach alle Backfacing Verts in Lichtlaufrichtung ausziehen. Die degenrierten Quads sorgen dann dafür, dass an der Silouhette keine Löcher entstehen weil sie dort zu richtigen Quads ausgezogen werden.

    Bei ATI gibt es ein Sample mit Code zum Download wo das so gemacht ist.

    Ciao,
    Stefan


Anmelden zum Antworten