Home

How-To: Shader Drehen mit Functions

Samstag, 22. September 2007 | Autor:

Vor kurzem wurde ich gefragt, wie man Heightfields rotieren kann. Tatsächlich bietet nicht einmal jeder Terrain-Editor die 90°-Rotation, die ja noch sehr einfach machbar ist. Auch TG2 selbst bringt diese Funktionen nicht mit. Lediglich eine 180°-Rotation bei externen Heightfields ist möglich, indem man beide Flip-Optionen setzt.

Mit der folgenden Methode kann man Heightfields, aber auch ganze Shader, um einen Winkel im Bereich (-90°, 90°) drehen. Das heißt außer -90° und 90° selbst. Rotationen um 90° oder Vielfache davon sollten in einem externen Editor stattfinden.

Idee und Realisierungrot_schema_12.png

Die Überlegung ist folgende: Wir können einem bestimmten Punkt im Terrain nicht einfach eine andere Position zuweisen, man kann die theoretische Position nach der Rotation natürlich berechnen und den Punkt dann verschieben. Das ist aber mit TG2 kaum machbar. Deswegen gehen wir einen anderen Weg.
In den Bildern rechts sind z- und x-Achse eingezeichnet, sowie ein Quadrat angedeutet. Die Skizzen beschreiben, was wir machen wollen. Zusätzlich befindet sich weiter unten ein Screenshot des Node-Networks der Rotation, auf dem die konkrete Implementation zu sehen ist.

rot_schema_22.pngSchritt 1: Je höher der z-Wert eines Punktes, desto weiter muss er in x-Richtung verschoben werden. Das machen wir mit Displacement (Lateral Only), welches wir per Redirect auf die x-Richtung einschränken. Die Achse wird dabei mitverschoben, dank Get Position in Geometry.
Der Winkel Alpha (α) ist der Winkel, um den wir den Shader drehen. Wie aus Alpha der exakte Multiplikator für die Verschiebung errechnet wird, wird weiter unten erklärt.

rot_schema_31.png

Schritt 2: Je höher der x-Wert, desto mehr Displacement in negative z-Richtung. Statt den Skalar vorher negieren, kann man im Displacement Shader den Multiplier auf -1 stellen, das spart einen Shader und somit Rechenzeit.
Voilà: Wir haben unseren Shader gedreht!

rot-clip-closeup.png

Multiplikator und Implementierungsdetails

Je höher der z-Wert ist, desto weiter wird in x-Richtung verschoben. Die Frage ist, wenn z beispielsweise 1m ist, wird dann auch um genau 1m verschoben? Das hängt natürlich vom Rotationswinkel ab, ist er größer, ist auch das Verhältnis von z zur Stärke des Displacements größer. Dieses Seitenverhältnis ist genau der Tangens des Drehwinkels. (Erklärung zu Tangens etc. gibt es auf Wikipedia)

Glücklicherweise bringt TG2 die Tangensfunktion schon von Haus aus mit. Allerdings arbeitet diese nicht mit Werten in Grad, sondern in Bogenmaß/Radiant. Dabei gilt: 180°=π(pi) rad. Da ich den Alpha-Skalar gerne anschaulich in Grad eingeben will, muss der Grad-Wert mittels Division durch 180°/π umgerechnet werden. (Man könnte übrigens auch mit π/ 180° multiplizieren.)

Die z- bzw. x-Werte müssen nur noch mit dem so gewonnenen Seitenverhältnis multipliziert werden. Danach werden sie durch Displacement Shader geschoben und einem Redirect Shader als Input gegeben. Dabei reicht ein einzener Redirect aus.

Theoretisch dürfte der zu rotierende Shader nicht direkt an den Redirect geschlossen werden, sondern müsste ein Compute Normal vorgeschaltet bekommen, da die Lateral-Only-Einstellung dies verlangt. Beim konkreten Beispiel konnte jedoch kein Unterschied zwischen Bildern mit und ohne Compute Normal festgestellt werden.

Praxis und Nebenwirkungen

Die Methode funktioniert tatsächlich recht zuverlässig. Die folgenden Bilder zeigen von links nach rechts: Das Bild ohne Rotation, das Bild mit Rotation um 0°, 45°, -45°. Dabei wurden Kamera und Sonne jeweils passend mit gedreht und das Terrain neu skaliert (siehe unten).

rotv_x.jpgrotv_0.jpgrotv_45.jpgrotv_-45.jpg

Zwischen 0°-Rotation und ausgeschalteter Rotation besteht kein sichtbarer Unterschied. Auch bei ±45° ändert sich das Terrain nicht. Unterscheide im Aussehen der Surface liegen daran, dass die dafür verantwortlichen Fractal Breakups nicht mit rotiert wurden, sondern nur das Heightfield.

In der Preview sieht eine Drehung um 45° folgendermaßen aus:

rot_preview.jpg

Wie man unschwer erkennen kann, ist das Heightfield nun in der Breite wesentlich größer als vorher – um den Faktor 1/cos(α) größer. Um dem entgegenzuwirken kann man einen Heightfield-Resize-Operator verwenden, der die Breite bzw. Länge des Heightfields auf Original-Breite · cos(α) verkleinert. Das muss von Hand ausgerechnet werden, wozu man übrigens Googles eingebauten Rechner verwenden kann. Heightfield-Resize verkleinert jedoch auch die Höhe – um das wieder rückgängig zu machen, multipliziert man mittels Adjust Vertical oder dem Height-Multiplier im Heightfield-Shader die Höhe mit 1/cos(α).

Diese Prozedur wird bei großen Winkeln immer stärker notwenig. Ab etwa 70° treten dann allerdings andere Nebeneffekte auf, wie etwa eine starke Abdunklung des Terrains und andere Begleiterscheinungen des extremen Displacements. Für große Rotationen sollte man sich daher zunächst mit externen Programmen in 90°-Schritten dem gewünschten Winkel nähern und anschließend mit der hier beschriebenen Methode den genauen Winkel einstellen.

Clip-File und Einbau

rotation-einbau1.pngDas Clip-File kann hier heruntergeladen werden. Der Screenshot rechts zeigt, wie es eingebaut wird.

Das war’s! Ich hoffe ich konnte die Methode anschaulich erklären. Kritik oder Erfahrungswerte sind wie immer willkommen. 🙂

Tags »

Trackback: