Ich glaube, hier liegt mal wieder ein Verständnisproblem vor.
Zuerst ist zu beachten, dass man verschiedene Koordinatensysteme hat. Das erste ist das Koordinatensystem in der HeightMap, das einfach das 2D Koordinatensystem von ganzen Zahlen des Bildes ist. Weil das dicht an einer Textur ist, nenne ich die Punkte dort einfach mal (
u,
v). An jeder solchen Koordinate ist eine Höheninformation
w =
h(
u,
v) gespeichert. Soweit war das sicher jedem klar.
Dann gibt es noch das 3D Weltkoordinatensystem, in dem sich die Kamera und die gezeichneten Dreiecke für das Terrain befinden. Die Kamera befindet sich an den Koordintaten (
cx,
cy,
cz). Das sind alles reelle Zahlen, die wir z.B. als float abbilden.
Zu jedem Pärchen [(
u,
v),
w] (das habe ich mit Absicht so geschrieben!) gibt es ebenfalls eine eindeutige Weltkoordinate (
X(
u),
Y(
v),
Z(
w))
Die Funktionen
X und
Y definieren, wie die 2D Punkte der HeightMap als Gitter in den 3D-Raum abgebildet werden und sind meistens einfache Skalierungen (
X(
u) = g *
u), also einfach umzukehren. Im einfachsten Fall nimmt man g = 1.0, dann kann man
u und
v direkt als
x und
y übernehmen (umgekehrt nicht, weil die Nachkommastellen einen Strich durch die Rechnung machen; später mehr dazu). Die Umkehrfunktionen nenne ich
U(
x) =
x / g und
V. Wichtig dabei ist, das
U und
V immernoch eine reele Zahl als Ergebnis haben.
Die Funktion
Z sieht generell genauso aus, kann aber auch komplizierter sein (das ist für diesen Fall nicht so wichtig).
Die eigentliche Aufgabe, so wie das Problem in den letzten paar Posts beschrieben wurde, ist nun folgende:
Finde zu
cx und
cy die vier Punkte (
cu_1,
cv_1), ..., (
cu_4,
cv_4), die Nachbarn von (
cu,
cv) = (
U(
cx),
V(
cy)) sind. Das ist die Prüf-Facette innerhalb der HeightMap. Dazu suche den nächstkleineren Ganzzahlwert von
cu und
cv (::floor), was den "linken oberen" Nachbarn (
cu_1,
cv_1) ergibt (den Nachkommateil ncu =
cu - ::floor (
cu) und ncv merkt man sich). Für die rechten Nachbarn ist die u-Koordinate um 1 höher und für die unteren ist die v-Koordinate um 1 höher.
Wenn man bei der Rechnung im negativen Bereich landet, oder Punkte erhält, die gar nicht mehr in der HeightMap liegen können, befindet sich die Kamera auch nicht mehr über der HeightMap und man kann auch nichts ausrechnen.
Damit kann man nun bestimmen, ob die Kamera "oberhalb" der Facette ist, indem man
cz mit dem Höhenwert
h(
cu,
cv) innerhalb der Facette vergleicht. Der ist nicht direkt definiert, weil
cu und
cv keine ganzen Zahlen sind, darum muss man den interpolieren (oder anders abschätzen).
Ein paar einfache Abschätzungen sind das Maximum, das Minimum der Höhenwerte der Nachbarn oder eben der Durchschnitt der vier Höhen. Für einfache Anwendungen, oder eine HeightMap, die hoch genug auflöst, ist das sicher nicht falsch.
Man kann aber auch den Wert aus den Höhen der Nachbarn bilinear interpolieren. Dazu braucht man die beiden Nachkommaanteile ncu und ncv.
|
Quellcode
|
1
2
3
4
|
h (cu, cv) = (1.0 - ncu) * (1.0 - ncv) * lo +
ncu * (1.0 - ncv) * ro +
(1.0 - ncu) * ncv * lu +
ncu * ncv * ru;
|
[l, r][o, u] steht für links / rechts oben / unten. (Das ist hier wirklich wichtig; wenn man die Reihenfolge durcheinanderbringt, liefert die Berechnung falsche Ergebnisse!)
Wie man leicht sieht, ist der Durchschnittswert der Höhen ein Spezialfall der bilinearen Interpolation, nämlich für ncu = ncv = 0.5. Technisch betrachtet kann man mit bilinearer Interpolation auch nur dann die exakte Höhe berechnen, wenn man die Facette entsprechend trianguliert. Dazu müsste man pro Facette einen "Fan" um den Durchschnittspunkt der Facette in Weltkoordinaten legen.
Normalerweise teilt man die Facetten aber nur in zwei Dreiecke auf, so dass bilineare Interpolation auch wieder nur eine Abschätzung (wenn auch eine bessere) ist. Will man das auch noch abbilden, bleibt das Prinzip aber das gleiche. Finde heraus, über welchem Dreieck der Facette man sich befindet, bestimmt aus
cx und
cy ein
tu und
tv, das die relative Position im Dreieck bestimmt, berechne damit den Höhenwert aus den drei Eckpunkten, und man ist fertig. Die Berechnung von
tu und
tv lasse ich mal als Übung für euch. (a.k.a. ich bin zu faul, das eben selbst herzuleiten
)
Gruss,
Rainer