Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

11

22.08.2014, 16:16

Wenig Erfahrung mit 2D Beleuchtung, aber du könntest folgendes machen:
Erstmal auf deferred rendering umsteigen und im "2D-Gbuffer" eine Art Depthbuffer zeichnen (du bezeichnest es als "Heightmap" - auch ok, kommt aufs selbe hinaus).
Direkte Beleuchtung wirst du relativ einfach lösen können, indem du noch einen Normalbuffer im GBuffer zeichnest und auf diesen (und der Heightmap, um die Position zu haben) mit bekannten Beleuchtungsmodellen im Lighting Pass zugreifst.

In der 3D Grafik kommt an dieser Stelle noch das Shadow mapping hinzu. Leider ist das in dem Fall etwas schwierig, da du nicht einfach die Szene aus einer anderen Perspektive rendern kannst, um Shadowmapping anzuwenden.
Mir fallen jetzt folgende Dinge ein, die du stattdessen machen kannst:
  • Eine art 2D-Shadow Volume: Polygone berechnen, die sich aus dem Objekt selbst und Linien, von den Eckpunkten aus in die Gegenrichtung zur Lichtquelle (siehe Illustration), zusammen setzen. Penumbra ließe sich dann durch einen Lowpass filter realisieren, wobei der Kernel mit zunehmender Entfernung von der Lichtquelle immer größer wird (dadurch wird die Penumbra auch entsprechend größer).
    Rot markiert sind die Eckpunkte des Schatten-Polygons. Blau markiert sind die Bereiche, die geblurred werden sollten, um Penumbra anzunähern (es gibt auch weitere Penumbra methoden (Random Rotated PCF kernel, Penumbra map, Gaussian Blur), siehe google). In der Illustration habe ich die vordersten Vertizes zur "Projektion" weiter hinten genutzt. Du kannst genauso gut auch die Hinteren nehmen, oder das Objekt einfach nochmal skaliert am ende des Schattens zeichnen.
    Dieses Polygon kannst du jetzt innerhalb deines Lighting-Passes nutzen, um Schattenregionen nicht zu beleuchten.
  • Wenn du die Vertices auf dem Boden der schattenwerfenden Objekte hast, kannst du sozusagen als "Helpers" dreidimensionale Klötze aufziehen und diese dann aus der Perspektive des Lichts dreidimensional rendern
  • Raycasting (für Realtime-Anwendungen im 3D Bereich unbrauchbar. Vllt. sieht es im 2D Bereich schon besser aus, aber vermutlich auch sehr unperformant)
  • Sweep-Line -- bin nur während des googlens drauf gestoßen. Ob es für Realtime Anwendungen sinnvoll ist weiß ich nicht.

Folgende Seite macht den Schatten ganz ähnlich wie in meinem ersten Punkt - ist sicher auch einen Blick wert:
http://archive.gamedev.net/archive/refer…adow/page3.html

Um zu schauen, ob der Schatten auf ein Pixel anzuwenden ist, meine ich, dass der Boden-basierte Ansatz reicht. Du prüfst dann einfach nur den Depthbuffer / Heightbuffer auf die Höhe. Wenn zu hoch / nicht "tief" genug, dann wendest du eben keinen Schatten an.

Ich hoffe das bringt dich etwas weiter.
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

12

24.08.2014, 16:16

Wo ist denn die Frage, auf die Du Dir Antwort erhoffst?

Bodenbasiert kann das natürlich so klappen. Beleuchtung kannst Du damit stressfrei zaubern: einfach die Höhe über Boden in einen GBuffer ausrendern, daraus und aus der Bildschirmposition des Pixels kannst Du die 3D-Koordinaten berechnen. Schatten kannst Du unabhängig davon auch bodenbasierend machen - entweder durch geometrische Konstruktion von Strahlen und Dreiecken, oder halt durch Raycasting. Ich hatte dazu ja meine Technik vorgestellt, und denke, dass sie in diesem Setting ebenso funktioniert.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

13

28.08.2014, 00:21

Vielen Dank für die freundliche Hilfe.

Wenn ich idontknows verlinkten Post richtig verstanden habe, dann wird im Shader für jedes zu beleuchtende Pixel und für jede Lichtlquelle ein line-of-sight-Test gemacht? Trifft das zu? Ist das tatsächlich performant und praktikabel?
Ich muss mich wohl dringend mal näher mit Shadern auseinandersetzen... :)

Also mein Problem mit dem Boden-Schatten-Gedöns ist vor allem,
dass das Licht sich "automatisch" auf die sichtbaren Wand-Flächen ausbreiten sollte, die beim 2D-Boden-Test natürlich sozusagen in ihrem eigenen Schatten liegen (Licht von links bzw. "unten")
bzw. dass umgekehrt Licht von rechts bzw. "oben" geblockt werden sollte, schon bevor es die Wand auf dem Boden erreicht.

Vielleicht stehe ich da aber auch auf dem Schlauch. Wenn ich durch die Heightmap die Höhe eines jeden Pixels habe, kann ich für jedes Pixel (einer Wand) ja logischerweise auch die Koordinaten auf dem Boden ausrechnen? Allerdings kann ich ja dann so direkt über meine Heightmap (quasi Höhe der Grafik-Pixel) keinen Line-Of-Sight-Test machen... Denn x,y sind ja keine Bodenkoordinaten. Ich müsste erst eine weitere Textur erstellen, die für jedes Boden-Pixel die maximale Höhe angibt. Hmm... das könnte man aus den Heightmaps berechnen; aber durch die Verdeckung hätte ich auf dem Boden ja viele fehlende Werte.

Außerdem kann ich ja dann nur schwerlich direkt eine maximale Entfernung zur Lichtquelle setzen, da ja wegen der Perspektive immer die größtmögliche Wandhöhe in Pixeln zusätzlich berücksichtigt werden muss.

Zitat

Die Schatten dürfen halt nicht sofort mit der Wand beginnen, sondern erst etwas weg von der Lichtquelle. Das könnte man mit einem minimalen Offset aber problemlos erledigen

Was meinst du damit? Warum? ?(

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »DerKlaus« (28.08.2014, 00:29)


idontknow

unregistriert

14

28.08.2014, 01:13

Im isometrischen kann ich dir nicht groß weiterhelfen, aber ja pro Lichtquelle machst du für jeden beleuchteten Pixel diesen Test. Mit Shadern sollte das so performant wie nur möglich sein (weiß nicht wie gut die mit Schleifen zurecht kommen, aber wenn du Verzweigungen minimierst sollte das ziemlich flott werden). Hab aber selber nie getestet wo das "Limit" der Performance ist. Vielleicht hat Schrompf da eher Erfahrungen gemacht.

15

28.08.2014, 13:29

Zitat

Also mein Problem mit dem Boden-Schatten-Gedöns ist vor allem,
dass das Licht sich "automatisch" auf die sichtbaren Wand-Flächen ausbreiten sollte, die beim 2D-Boden-Test natürlich sozusagen in ihrem eigenen Schatten liegen (Licht von links bzw. "unten")
bzw. dass umgekehrt Licht von rechts bzw. "oben" geblockt werden sollte, schon bevor es die Wand auf dem Boden erreicht.

Du scheinst mich nicht richtig zu verstehen oder dir fehlt noch das Grundwissen in Sachen Shadern.
Das direkte Licht realisierst du mit einer Fullscreen-Normalmap. Die Normalen zeigen dann immer in die sichtbare Richtung. Wenn dann der Winkel zwischen Einfallsvektor und Normale größer als 90° ist, bekommt dieses Pixel einfach kein Licht.
Den Schatten renderst du, indem du eine 2D Shadowmap mithilfe von zweidimensionalen Formen, die Schatten erzeugen. Danach führst du die Shadowmap mit deiner direkt-beleuchteten Szene zusammen, indem du die Heightmap nutzt. Du nimmst dir einfach einen Wert, der den Boden repräsentiert (bspw. 0). Dann fügst du die Shadowmap an einem Pixel nur dann hinzu, wenn der Heightmap-Wert an dieser Stelle == 0 ist. Somit liegen die Wände also auch nicht in ihrem eigenen Schatten.
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

16

28.08.2014, 17:31

Ah, klar, dass die Normalen eine Beleuchtung der Wände quasi "von hinten" verhindern, stimmt natürlich... :rolleyes:

Dann könnte das tatsächlich gehen.

Zitat

Den Schatten renderst du, indem du eine 2D Shadowmap mithilfe von zweidimensionalen Formen, die Schatten erzeugen. Danach führst du die Shadowmap mit deiner direkt-beleuchteten Szene zusammen, indem du die Heightmap nutzt. Du nimmst dir einfach einen Wert, der den Boden repräsentiert (bspw. 0). Dann fügst du die Shadowmap an einem Pixel nur dann hinzu, wenn der Heightmap-Wert an dieser Stelle == 0 ist. Somit liegen die Wände also auch nicht in ihrem eigenen Schatten.

Öh... muss ich nicht eher aus den Pixelkoordinaten + Höhenangabe die jeweilige Bodenkoordinate (der Pixelsäule) ausrechnen und das dann in der 2d-Boden-Shadowmap prüfen? Oder hast du das gemeint?

Dann wäre jetzt noch die Frage, ob es empfehlenswert ist, quasi brute-force line-of-sight checks im Shader zu machen. Dann könnte ich mir ja die geometrische shadow-map-Erzeugung sparen und stattdessen einfach eine Textur mit den "Grundrissen" der Hindernisse nehmen.

17

28.08.2014, 18:30

Zitat

Dann könnte ich mir ja die geometrische shadow-map-Erzeugung sparen und stattdessen einfach eine Textur mit den "Grundrissen" der Hindernisse nehmen.

Ich meine genau, dass du mithilfe von Formen auf dem Boden (!!), ob nun als Textur gespeichert oder Vertexbuffer oder .., eine zweidimensionale Shadowmap erzeugst. Eine Line-of-sight Methode bringt nur noch mehr schwierigkeiten mit sich.
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

18

28.08.2014, 19:30

Nö, Line Of Sight wäre genau mein Vorschlag.

- Du renderst den Grundriss aus. Also nicht das Bild mit Diagonale, sondern direkt von oben ohne Perspektive und alles.

Ergebnis: eine Textur, in der jeder Texel einen gewissen kleinen Bodenbereich des Bildschirms beschreibt und darin dessen Höhe steht.

Von der Seite betrachtet sähe das so aus: pro Pixel eine Höhe, Lichthöhe ist auch bekannt, Beispielbild:



- in eine separate Textur: Du gehst im Shader von jedem Pixel aus eine Reihe Texel in Richtung Lichtquelle. Jede Höhe, die Du aus dem Texel liest, bedeutet eine Schattenkante, die von der Lichthöhe bis zur Texelhöhe geht und dahinter linear fortgesetzt wird. Ganz banale Geradenfunktion mit Anstiegsparameter, wie Du es aus dem Matheuntericht kennst.

- Im Shader kannst Du ausrechnen, welche Höhe diese Gerade an der Stelle hätte, für die der PixelShader gerade läuft. Speichere das Maximum aller Höhen in der Textur.

Wieder Beispielbild, wie das von der Seite betrachtet aussehen könnte. Die roten Höhenwerte gibt der Shader als Ergebnis aus, die werden in das Rendertarget geschrieben.



- in eine andere separate Textur: Du gehst wieder von jedem Pixel aus eine Reihe Texel in Richtung Lichtquelle, aber dieses Mal mit vierfach größerer Schrittweite. Das reicht aus, denn Du musst ja nur eine der Schattenkanten des vorherigen Passes wiederfinden.

Wieder Beispielbild, wie das von der Seite betrachtet aussieht. Für den hinteren Pixel läuft der Shader, die acht gelesenen Texel aus der vorherigen Schattenkanten-Textur sind grün eingezeichnet. Die höchste Kante wird wieder vom Shader ausgegeben, das ist die gelbe Höhe.



Und da sieht man sehr hübsch, wie das Kaskadieren funktioniert: mit jedem Pass machst Du xfach größere Schritte durch die vorherige Textur, um die alte Schattenkante wiederzufinden. Da Du immer die Schattenkante nimmst, die die höchste Höhe am Zielpixel hat, bekommst Du automatisch das höchste Hindernis und verlängerst so pro Pass alle Schattenkanten auf das xfache.

In Programmiererdeutsch: der lineare Sichtlinientest vom Wunschpixel zur Lichtquelle ist O(n), die kaskadierende Lösung ist nur noch O(log n).

Und wenn Du damit ein bisschen Ping Pong zwischen zwei Rendertargets gemacht hast, ist das Ergebnis eine Schattentextur, in der in jedem Pixel drin steht, wie hoch an dieser Stelle die Grafik sein muss, um Licht von der aktuell betrachteten Lichtquelle zu bekommen. Du kannst diese Höhe dann im Deferred Renderer dann gegen die Höhe des Pixels vergleichen oder gegen die Maximalhöhe, damit die Wände sich nicht selbst schattieren.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

19

28.08.2014, 20:50

Mhm. Das Problem dabei ist vermutlich, dass er, wenn ich es richtig verstanden habe, die Höhen schon projiziert speichert und somit die "Seitenansicht" zwar logisch aussieht, aber in der Praxis nicht umsetzbar ist, da er wie gesagt keine zum Boden orthogonale Höhe hat, sondern bereits projizierte Höhen.

So, meine ich, dass seine Heightmap von der Seite aussieht:



Das riecht mir btw auch nach sehr vielen Samples, die einen dynamischen Schatten vermutlich sehr inperformant machen würden. Meine ich.
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »iSmokiieZz« (28.08.2014, 21:09)


Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

20

28.08.2014, 21:36

Nö, Du musst nur die 2D-Bodenposition jedes Pixels auf dem Bildschirm eindeutig zuordnen können. Und das kannst Du ja einfach ausrendern wie bei einem Deferred Renderer.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

Werbeanzeige