}

Halb dynamisches Lightmapping in Unity 3D

Aktuell entwickle ich in meiner Freizeit ein 2D Geschicklichkeits-Puzzle-Spiel mit Unity. Obwohl das Spiel nur eine 2D Perspektive bietet ist es in 3D umgesetzt. Dies ermöglicht oder erleichtert manche  Effekte, z.B. Lichteffekte. Bei meinem Spiel gibt es in manchen Levels mehr als ein Dutzend Spielsteine, die durch den Spieler zum Leuchten gebracht werden müssen. Zunächst habe ich hierzu einfache Unitys Point Lights verwendet. Dies hat mit Per-Pixel-Lighting gut funktioniert, benötigte aber - ohne deferred Rendering - unnötig viel Grafikleistung. Gerade auf Mobile Devices ist dies nicht erwünscht: Es erhöht die Abwärme und reduziert die Akkulaufzeit. Zur Verwendung von Vertexlighting sind es zuviele Lichter die die gleichen Objekte beleuchten. Hierbei werden nur 8 Lichtquellen je Objekt berücksichtigt. Außerdem fand ich damit das Ergebnis visuell nicht überzeugend. Schließlich habe ich selbstgebaute Lightmaps verwendet, die den eigentlichen Objekten überlagert werden. Für die eigentlichen Objekte wurde die Beleuchtung vollständig deaktiviert (Unlit). Dazu wird eine spezielle Textur auf flachen Quadraten hauch dünn über dem eigentlichen Spielfeld platziert. Die Textur wird mit einem entsprechenden Custom Shader gerendert. Durch aktivieren und deaktivieren dieses Lightmap-Quadrats wird das ein und Ausschalten des Leuchtens simuliert. Das Spezielle an der Textur ist die Verwendung der Intensitätswerte je Farbkanal (Rot, Grün, Blau). Diese haben einen Wertebereich von 0,0 (Farbkanal dunkel) bis 1,0 (maximale Intensität des Farbkanals). Bei der Lightmap entspricht eine Intensität von 0,5 keine Veränderung der Helligkeit durch die Lightmap. Eine Intensität größer 0,5 sorgt für eine Aufhellung, eine Intensität kleiner 0,5 sorgt für eine Abdunkelung. Dabei erzeugt eine Intensität von 0,0 eine vollständige Verdunkelung, ein Wert von 1,0 eine maximale Erhellung. Beispiel Lightmap Beispiel Lightmap Im Bildbearbeitungsprogramm steht ein mittleres Grau für Stellen in der Lightmap die unverändert bleiben. Helles Weiß für voll mit Weiß ausgeleuchtete Stellen. Helles Rot für nur mit Rot ausgeleuchtete Stellen. Helles Grün für mit Grün ausgeleuchtete Stellen, usw. Der spezielle Shader ist eigentlich recht einfach. Wichtig ist, dass er in die Render Queue Transparent gestellt wird, damit die Lightmaps erst nach allen anderen Objekten gerendert werden. Da nur bereits gerenderte Objekte aufgehellt oder abgedunkelt werden können. Im Shader selbst wird für die Aufhellung oder Abdunkelung schlicht Blend DstColor SrcColor verwendet. Dadurch wird je Farbkanal zweimal die bereits auf dem Bildschirm vorhandene Intensität mit der aus der Lightmap multipliziert. Die beiden Multiplikationsergebnisse werden aufaddiert. Vereinfacht ausgedrückt ist die Formel je Farbkanal 2*Bildschirm*Lightmap. Das hierbei errechnete Ergebnis wird auf den Bereich 0,0 bis 1,0 eingeschränkt. Wenn es größer 1,0 ist wird es schlicht auf 1,0 gesetzt. Wenn sich mehrere Lightmaps auf dem Bildschirm überlappen muss man darauf achten, dass diese minimal unterschiedlich weit von der Kamera entfernt sind, da es sonst zu unschönen visuellen Artefakten durch Z-Fighting kommen kann. Der Vollständige Code des verwendeten Shaders sieht wie folgt aus:

Shader “Custom/ExtraLightmap” {
     Properties {
         _MainTex (“Base (RGB)”, 2D) = “white” {}
     }
     SubShader {
         Tags {“Queue”=”Transparent” “RenderType”=”Transparent”}
         Pass {
             Blend DstColor SrcColor
             SetTexture { combine texture }
         }
     }