Archiv für März 2009

Projektstatistik 03/2009

Dienstag, 31. März 2009

Ja heftig! Diesen Monat hab ich ja richtig was geschafft! Viel sagen möchte ich da jetzt gar nicht zu, lest euch lieber den ersten Teil über Shader- und Rendering-System durch. Bis demnächst.

Zeilen gesamt Nur Code Nur Kommentare Code mit Kommentaren Leerzeilen Nicht-Leerzeilen
105.514 44.977 33.283 2.869 24.385 81.129
100 % 43 % 32 % 3 % 23 % 77 %

(Zur vorigen Tabelle geht es hier.)

Shader- und Rendering-System (I/II)

Montag, 30. März 2009

Puh! Das war was! Das Rendering-System ist nun auch fast fertig. Da es in letzter Zeit nicht allzu viel zu zeigen gab, möchte ich nun das Shader- und insbesondere das Rendering-System etwas genauer vorstellen. Schließlich habe ich mich ja genau damit in der letzten Zeit befasst. Damit der Rahmen hier nicht gesprengt wird und ich nicht alles Pulver auf einmal verschieße, mache ich mehrere Teile. Dieses ist der erste Teil. Ganz fertig ist das Rendering-System ja auch — wie gesagt — noch nicht, insofern ist eine Aufteilung ja ganz sinnvoll.

Struktur und Funktionalität des Rendering-Systems, was auf Teile des Shader-Systems zurückgreift, möchte ich anhand einer kleinen Grafik erklären:

Rendering-System

Kern des Rendering-Systems bilden Chain, Effect und Pass. Generell gibt es pro darzustellender Szene ein Chain-Objekt, dass die “Kette” an Effekten (Effect-Objekten) repräsentiert, die letztlich das Bild sukzessiv aufbauen. Ein Effekt kann dabei wiederum aus mehreren Pass-Objekten zusammengesetzt sein. Für einen Cel-Shading-Effekt (Cel = Contour Enhancing Lines) werden beispielsweise vielleicht drei Durchgänge (Passes) benötigt, was ich gleich noch genauer erläutern und anhand von Screenshots verdeutlichen werde. Pro Chain- und Effect-Objekt werden verschiedene Render-Targets zur Verfügung gestellt. Schließlich soll ja im Falle von mehreren Effekten bzw. mehreren Durchgängen eines Effekts nicht direkt in den Bildschirmspeicher, sondern in weiterverwertbare Render-Targets in Form von Texturen gezeichnet werden. Diese Render-Targets werden also entsprechend von den Chain- und Effect-Objekten verwaltet und den Pass-Objekten zur Verfügung gestellt. Ein Pass-Objekt greift dann auf ein Paar von Pixel- und Vertex-Shadern zurück (Shader-Combo), um die Geometrie der Szene letztlich zu transformieren und als Farb- und Tiefeninformationen in die Render-Targets zu zeichnen. Welche Eigenschaften der Geometrie zum Zeichnen mit auf den Weg gegeben werden, ist in der Vertex-Declaration gespeichert.

Um das noch ein bisschen zu verdeutlichen, kommt jetzt mein Cel-Shading-Beispiel:

Cyber E-Razor ist ja eine Comicfigur. Eine Comicfigur wird natürlich in gewissem Comicstil gezeichnet. So mit schwarzer Umrandung und wenigen (aber dafür umso markanteren) Farbabstufungen und so. Im Folgenden geht es darum, wie ich an diese schwarzen Konturlinien komme. Das endgültige Resultat zeige ich ein andermal. Im Moment geht es nur um die Konturlinien. Zur Extraktion der gewünschten Linien verfolge ich einen Ansatz von Mitchell et al., “Real-Time Image-Space Outlining for Non-Photorealistic Rendering”, den ich natürlich meinen Bedürfnissen angepasst habe. Dabei werden die Konturlinien aus drei “Informationen” der darzustellenden Objekte zusammengestellt: Die Farbe, der Normalenvektor (die Ausrichtung der Objektoberfläche) und die Tiefe (die Entfernung zur Kamera). Dafür wird die gesamte Szene erstmal in zwei Texturen gezeichnet: Eine soll die Farben enthalten. Die andere soll die Normalenvektoren in den drei Farbkanälen Rot, Grün und Blau sowie die Tiefenwerte im Alphakanal enthalten. Mit der zweiten Textur schlagen wir also zwei Fliegen auf einen Streich. Wie diese Texturen dann aussehen, ist in folgenden zwei Screenshots zu sehen:

Color-Map

Normal-Depth-Map

Ich habe zwar extra eine Perspektive gewählt, bei der Cyber seine heftige Gun Richtung Kamera streckt, damit die Tiefeninformationen möglichst stark zur Geltung kommen. Der rechte Screenshot ist aber trotzdem nicht so ganz aussagekräftig, daher hier noch mal zwei Screenshots, die die Normalenvektoren und die Tiefeninformationen getrennt voneinander beinhalten:

Normal-Map

Depth-Map

Sau geil! Die Normalenvektoren werden ja in den Rot-, Grün- und Blaukanälen der Textur gespeichert, insofern sieht Cyber auf dem linken Screenshot derbe bunt aus. Die XYZ-Komponenten der Normalenvektoren werden hierfür einfach als RGB-Farbwerte in die Textur geschrieben. Das passt ja ganz gut, sind ja schließlich jeweils drei Werte, XYZ oder halt RGB. Daher wird man aus der gegebenen Perspektive auch niemals die Farbe Blau sehen, weil die blauen Pixel alle in die andere Richtung “zeigen”, von der Kamera weg. Im rechten Screenshot sind die Tiefeninformationen gespeichert, je heller desto größer die Entfernung zur Kamera. Diese Informationen waren im oberen rechten Screenshot ja im Alphakanal der Textur gespeichert. Dort erscheinen die Farben daher etwas dunkler, weil sie wegen der “Transparenz des Alphakanals” mit dem schwarzen Hintergrund gemischt werden. Auch hier gilt wieder: je “heller” desto größer die Entfernung zur Kamera, wobei “hell” einen höheren Alphawert und demnach geringere Transparenz bedeutet.

Ballert man das alles nun zusammen und lässt noch einen weiteren entsprechenden Shader drüber laufen, kommt etwas Schönes bei raus. Nämlich Folgendes: Aus den drei Informationen, Farbe, Normalenvektor und Tiefe, bauen wir uns nun die Konturlinien. Und zwar soll an jeder Pixelposition des resultierenden Bildes eine Konturlinie gezeichnet werden, wenn die drei gegebenen Informationen relativ stark von den jeweiligen Werten benachbarter Pixel abweichen. Das bedeutet also:

  • Weicht die Farbe stark von den Farben benachbarter Pixel ab: Konturlinie zeichnen.
  • Hat der Normalenvektor einen weitaus anderen Winkel als die Normalenvektoren benachbarter Pixel: Konturlinie zeichnen.
  • Ist der Tiefenwert heftig näher an oder weiter entfernt von der Kamera als die Tiefenwerte benachbarter Pixel: Konturlinie zeichnen.
  • Ansonsten: Keine Konturlinie zeichnen.

Das führt dann als Resultat zu richtig schönen Konturlinien, die so ziemlich alle Eigenschaften besitzen, wie man sie von Comicbildern erwartet:

Color-Map

Sehr schön! Gut zu erkennen ist, dass einige Linien dicker sind als andere. Welche das sind, ist noch nicht völlig optimal, aber tendenziell sieht das schon ganz gut aus: Die tatsächliche Umrandung der Figur ist weitestgehend dick gezeichnet, während die Linien “innerhalb” der Figur (hach, dafür gibt es doch auch einen Begriff?) meistens dünner erscheinen.

Wie gesagt: Bisher handelt es sich hier nur um die Konturlinien. Für das fertige Bild können noch Beleuchtung und Schatteneffekte oder sonst was Verwendung finden. Die Konturlinien werden dann zum Schluss quasi über das eigentliche Bild drübergelegt, um den Comicstil zu realisieren.

Hui, jetzt brennen mir aber wieder die Finger! — Als näxtes werde ich das Rendering-System noch um die Funktionalität eines so genannten Szenegraphen erweitern, der u.a. für eine geeignete Sortierung der darzustellenden Objekte zuständig ist. Dabei sollen die verschiedenen Geometrieeigenschaften gruppiert und somit der Rendering-Prozess beschleunigt werden. Wenn das soweit ist, kommt der zweite Teil zur Vorstellung von Shader- und Rendering-System. Dann wird es natürlich wieder neue Screenshots geben. Tschau!