Projektziel: In FlightGear sah das Wasser bei mir oft grün aus, mal in Seen, mal im Meer. Das fiel besonders auf, seit daneben echte Luftbilder liegen. Dahinter stecken zwei getrennte Ursachen, und beide lassen sich im Shader beheben. Unterwegs habe ich erst falsch gelegen und dann falsch verglichen, auch das steht hier.

Dunkelgrüner See neben Luftbildern in FlightGear
Bild 1 - Ausgangslage: Der See bei Leipzig ist dunkelgrün
Derselbe See in FlightGear, jetzt blau
Bild 2 - Derselbe See mit fester Wassertiefe

Ursache 1: Seen stehen auf einer Weltkarte

Der Wasser-Shader water-ALS-high.frag mischt flaches Wasser mit einer Bodenfarbe. Beide Werte kommen aus groben Weltkarten:


vec4 depthTexel = texture2D(topo_map, TopoUV);
shallowness = depthTexel.r;
...
vec4 colorTexel = texture2D(water_colormap, TopoUV);
floorColour = colorTexel.rgb;
...
refl.rgb = mix(refl.rgb, 0.65 * floorColour, floorMixFactor);

Ein kleiner See liegt auf einer so groben Karte offenbar noch auf Land, also kommt Grün dazu. Für Binnengewässer gibt es aber schon die passenden Schalter in Effects/water-inland.eff:


<use_specified_water_shallowness type="int">1</use_specified_water_shallowness>
<water_shallowness type="float">0.3</water_shallowness>

Der Wert 0,3 ist nicht beliebig: Darunter wird das Wasser dunkler, darüber kommt die Bodenfarbe zurück. Genau hier fällt sie ganz weg, und die Helligkeit bleibt voll.

Ursache 2: Tiefe Sonne

Danach war das Wasser beim Shuttle-Start am Kennedy Space Center trotzdem grün, diesmal auf einer großen Fläche. Erst dachte ich, die Fläche zähle als Meer und meine Korrektur erreiche sie nicht. Eine feste Tiefe auch fürs Meer machte das Wasser aber nur dunkler. Der Vergleich derselben Stelle zu verschiedenen Uhrzeiten brachte die Antwort:

Zeit (31.10., Florida)Mittlere Farbe des Wassers (R / G / B)Eindruck
12:00 GMT, tiefe Sonne52 / 69 / 49grün
14:00 GMT69 / 101 / 106blau
17:00 GMT69 / 101 / 106blau
22:00 GMT, tiefe Sonne54 / 77 / 65grünlich

Es liegt also am Sonnenstand. Im Vertex-Shader wird das Licht je Farbkanal einzeln berechnet, und bei tiefer Sonne fällt Blau am stärksten ab. Das ist als warmes Morgen- und Abendlicht gedacht und passt für Land. Ausgerechnet aus den Formeln im Shader:

lightArgRotGrünBlau
12 (Sonne hoch)0,930,940,87
40,770,620,31
00,600,260,03

Die Grundfarbe des Wassers ist aber 0,148 / 0,27 / 0,3, sie enthält kaum Rot. Der Shader multipliziert beides. Das Licht liefert kein Blau, das Wasser kein Rot, und übrig bleibt Grün.

Die Korrektur macht das Licht vor der Multiplikation neutraler, nur für das Wasser:


float light_helligkeit = dot(ambient_light.rgb, vec3(0.3, 0.59, 0.11));
ambient_light.rgb = mix(ambient_light.rgb, vec3(light_helligkeit), 0.7);
finalColor *= vec4(ambient_light.rgb + secondary_light * light_distance_fading(dist), ambient_light.a);
Olivgrünes Meer bei tiefer Sonne in FlightGear
Bild 3 - 12:00 GMT ohne Korrektur: 52 / 69 / 49
Dasselbe Meer mit Korrektur, blaugrün
Bild 4 - Mit Korrektur: 49 / 68 / 65. Um 14:00 GMT bleibt es unverändert bei 69 / 101 / 106

Die falsche Gegenprobe

Um zu sehen, ob das auch ohne meine Änderungen auftritt, habe ich FlightGear mit einem zweiten --fg-root aufs Original gerichtet. Die Farben waren exakt gleich, also hielt ich die Frage für geklärt. Aufgefallen ist der Fehler erst an den Erdbildern, die im Test „ab Werk“ genauso scharf waren wie meine eigenen. Mein Starter setzte schon ein --fg-root, und das gewann. Erst der Lauf mit einem wirklich anderen Starter zeigte echte Originalbilder. Das Ergebnis blieb zum Glück gleich: Auch das Original ist bei tiefer Sonne grün (52 / 69 / 49).

Ist das ein Fehler in FlightGear?

Die Lichtkurven sehen nach Absicht aus, grünes Wasser eher nach einer Nebenwirkung. Belegen kann ich das nicht. Echtes Wasser spiegelt bei flachem Blick vor allem den Himmel und wird dadurch nicht einfach grün. Mit der Korrektur wirkt das Wasser abends auch etwas weniger warm, und geprüft habe ich sie bisher in Standbildern.

Selbst ausprobieren

Beides lässt sich einbauen, ohne die FlightGear-Dateien anzufassen, über ein Overlay. Die Anleitung und ein Skript dazu stehen in der FlightGear-Doku.

Fazit

Gleiche Farbe, zwei Ursachen: einmal eine grobe Karte, einmal das Licht. Geholfen hat die einfachste Messung, dieselbe Stelle zu zwei Uhrzeiten. Und eine Gegenprobe ist nur so viel wert wie die Prüfung, ob sie wirklich das Original getestet hat.