Sie sind auf Seite 1von 96

Bachelorarbeit zum Thema

Untersuchung des Funktionsumfanges und der


Leistungsfähigkeit der Grak-Engine OGRE3D

zur Erlangung des akademischen Grades

Bachelor of Science

vorgelegt dem

Fachbereich Informatik, Mathematik und Naturwissenschaften der

Hochschule für Technik, Wirtschaft und Kultur Leipzig

von

Lars Bilke

24. Oktober 2006

Betreuer: Prof. Dr.-Ing. habil. Dieter Vyhnal


Eidesstattliche Erklärung
Hiermit versichere ich, die vorliegende Arbeit selbstständig und unter ausschlieÿlicher
Verwendung der angegebenen Literatur und Hilfsmittel erstellt zu haben.

Die Arbeit wurde bisher in gleicher oder ähnlicher Form keiner anderen Prüfungsbehörde
vorgelegt und auch nicht veröentlicht.

Markranstädt, den 24. Oktober 2006

2
Inhaltsverzeichnis

Abbildungsverzeichnis 6

1 Einführung 7
1.1 Einführung 3D-Engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.1.1 Begrisklärung 3D-Engine . . . . . . . . . . . . . . . . . . . . . . 8
1.1.2 Begriserklärung Hardware-Abstraktion . . . . . . . . . . . . . . 8
1.1.3 Geschichtliches . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.1.4 Unterschiede zwischen freien und kommerziellen 3D-Engines . . . 9
1.2 Freie 3D-Engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.1 Irrlicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.2 Nebula Device 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.3 Panda3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.4 Vergleichende Betrachtungen zu OGRE 3D . . . . . . . . . . . . . 13
1.3 Kommerzielle 3D-Engines . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.3.1 Torque Game Engine . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.3.2 CryEngine 1/2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.3 Unreal-Engine 2/3 . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.3.4 Source-Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2 Leistungsumfang von OGRE 3D 20


2.1 Generelle Leistungsmerkmale . . . . . . . . . . . . . . . . . . . . . . . . 20
2.2 Unterstützte Plattformen und 3D-APIs . . . . . . . . . . . . . . . . . . . 20
2.3 Material- und Shader-Unterstützung . . . . . . . . . . . . . . . . . . . . 21
2.4 3D-Geometrien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.5 Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.6 Szenenverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.7 Spezialeekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.8 Weitere Leistungsmerkmale . . . . . . . . . . . . . . . . . . . . . . . . . 23

3 Einführung in die OGRE 3D-Programmierung 24


3.1 Kernobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.1 Root-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.1.2 RenderSytem-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.1.3 SceneManager-Objekt . . . . . . . . . . . . . . . . . . . . . . . . 26
3.1.4 ResourceManager-Objekt . . . . . . . . . . . . . . . . . . . . . . . 27

3
3.1.5 Mesh-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.1.6 Entity-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.1.7 SceneNode-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.1.8 Materials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.1.9 Overlays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2 Einrichten einer Entwicklungsumgebung und Erstellen eines OGRE 3D-
Projekts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.3 Eine Hello World-Anwendung . . . . . . . . . . . . . . . . . . . . . . . 32
3.4 FrameListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.5 Ein interaktives Beispiel: Kamera- und Charaktersteuerung . . . . . . . . 39
3.5.1 Kamerasteuerung . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.5.2 Charaktersteuerung . . . . . . . . . . . . . . . . . . . . . . . . . . 41

4 Das OGRE 3D-Skriptsystem 44


4.1 Material-Skripte und Shader . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.1.1 Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.1.2 Passes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.1.3 Shader in OGRE 3D . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.1.4 Ein einfacher Beispielshader . . . . . . . . . . . . . . . . . . . . . 49
4.2 Compositor-Skripte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.2.1 Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.2.2 Target Pass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.2.3 Compositor Pass . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.2.4 Ein einfacher Beispiel-Kompositor . . . . . . . . . . . . . . . . . . 54
4.3 Particle-Skripte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.3.1 Partikelemitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.3.2 Partikelaektoren . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.3.3 Ein einfaches Beispiel-Partikelskript . . . . . . . . . . . . . . . . . 58
4.4 Overlay-Skripte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.4.1 Elemente zu Overlays hinzufügen . . . . . . . . . . . . . . . . . . 60
4.4.2 Standard-Overlay-Elemente . . . . . . . . . . . . . . . . . . . . . 60
4.4.3 Font Denition-Skripte . . . . . . . . . . . . . . . . . . . . . . . . 61

5 Schatten in OGRE 3D 63
5.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.1.1 Schatten in OGRE 3D aktivieren . . . . . . . . . . . . . . . . . . 63
5.1.2 Modulative Schatten . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.1.3 Additive Light Masking . . . . . . . . . . . . . . . . . . . . . . . 64
5.2 Stencil-Schatten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.2.1 Das HelloWorld-Beispiel mit Stencil-Schatten . . . . . . . . . . . 65
5.3 Textur-basierte Schatten . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

6 Animation in OGRE 3D 67
6.1 Skeletale Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

4
6.2 Vertex-Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.2.1 Morph-Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.2.2 Pose-Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.2.3 Kombination von skeletaler und Vertex-Animation . . . . . . . . . 71
6.3 Szenenknoten-Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.4 Animation von numerischen Werten . . . . . . . . . . . . . . . . . . . . . 72

7 OGRE 3D-Tools und Exporter 73


7.1 XML-Konverter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.2 MeshUpgrader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7.3 MeshViewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
7.4 ParticleEditor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.5 Mesh-Exporter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.5.1 3ds Max-Exporter . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7.5.2 Blender-Exporter . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7.6 oFusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7.7 RenderMonkey-Shader Exporter . . . . . . . . . . . . . . . . . . . . . . . 79

8 Ausblick 81

A Quellcode 82
A.1 HelloWorld-Quellcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
A.2 Eine interaktive Anwendung-Quellcode . . . . . . . . . . . . . . . . . . . 85
A.3 Compositor: Weichzeichner-Materialskript . . . . . . . . . . . . . . . . . 90
A.4 Compositor: Weichzeichner-Pixelshader . . . . . . . . . . . . . . . . . . . 91
A.5 Beispielshader: Vertexshader . . . . . . . . . . . . . . . . . . . . . . . . . 92
A.6 Beispielshader: Pixelshader . . . . . . . . . . . . . . . . . . . . . . . . . . 93

B Inhalt der Begleit-CD 94

Literaturverzeichnis 95

5
Abbildungsverzeichnis

1.1 Die Komponenten einer Spiel-Engine (in Anlehnung an [Mot03], Seite 495) 10
1.2 Screenshot von dem noch nicht veröentlichten Nebula Device Spiel Dra-
kensang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3 Disneys Toontown Online . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4 Freie Engines im Vergleich . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.5 Marble Blast Ultra auf der XBox 360 . . . . . . . . . . . . . . . . . . . . 15
1.6 Screenshot aus einer Crysis-Demonstration . . . . . . . . . . . . . . . . . 16
1.7 Normal Mapping in der Unreal Engine 3 . . . . . . . . . . . . . . . . . . 17
1.8 Lebensecht wirkende Charaktere im Spiel Half-Life 2 . . . . . . . . . . . 19

3.1 Die OGRE-Kernobjekte und ihre Beziehungen untereinander (in Anleh-


nung an [The06b]) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.2 Das HelloWorld-Renderfenster . . . . . . . . . . . . . . . . . . . . . . . . 37
3.3 Das Renderfenster der interaktiven Anwendung . . . . . . . . . . . . . . 43

4.1 Der PerVertex-Lighting Beispielshader . . . . . . . . . . . . . . . . . . . 52


4.2 Ein einfacher Weichzeichner . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.3 Ein einfaches Partikelsystem . . . . . . . . . . . . . . . . . . . . . . . . . 59

5.1 Das HelloWorld-Beispiel mit Stencil-Schatten . . . . . . . . . . . . . . . 65


5.2 Textur-basierte Schatten in OGRE . . . . . . . . . . . . . . . . . . . . . 66

6.1 Gesichtsanimation mit Pose-Animation . . . . . . . . . . . . . . . . . . . 70

7.1 Auswirkung der Detailreduktion des XML-Konverters auf die Texturpro-


jektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7.2 Der OGRE MeshViewer . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
7.3 Der OGRE ParticleEditor . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.4 Die OGRE-Renderansicht innerhalb von 3ds Max . . . . . . . . . . . . . 78
7.5 Ein Bump-Mapping Shader in RenderMonkey von ATI Technologies . . . 79
7.6 Ein einfacher Specular Lighting Shader mit einer Holztextur . . . . . . . 80

6
1 Einführung

Die Entwicklung der Computergrak, insbesondere der Echtzeit-3D-Grak, hat in letz-


ter Zeit eine rasante Entwicklung erlebt. In den letzten 5 Jahren hat sich die Grak-
Rechenleistung, gemessen in Pixeln pro Sekunde, alle 6 Monate verdoppelt, das ent-
spricht einem Faktor von etwa 1000! [Fer04]
Aber nicht nur die Leistungsfähigkeit sondern auch die Qualität und die Flexibilität der
Grakprogrammierung haben sich deutlich verbessert. Die ersten Grakbeschleuniger
wurden durch völlig frei programmierbare Grakprozessoren ersetzt, die es ermöglichen,
Grakalgorithmen und -eekte, die bisher Nicht-Echtzeit-Systemen vorbehalten waren,
nun auch in Echtzeit zu benutzen. Interaktive Anwendungen in Echtzeit kommen quali-
tativ immer näher an vorberechnete Animationssequenzen oder auch computergenerierte
Spiellme heran.
Die Frage ist, wie kann man die Fähigkeiten heutiger Computer mitsamt ihren Grak-
prozessoren am eektivsten nutzen?
Grak- APIs
1
wie Direct3Doder OpenGL
bieten einen einfachen Einstieg in die Gra-
kprogrammierung und direkten Zugri auf die Hardware. Sie bieten aber bei weitem
kein Grundgerüst für eine komplexe Anwendung, denn diese benötigt Dinge wie Unter-
stützung verschiedener Plattformen, Ressourcen- und Szenenverwaltung, Sichtbarkeits-
algorithmen, eine Möglichkeit zum Austausch von Daten mit anderen Anwendungen, die
Inhalte für die Engine erstellen usw.
Die frei erhältliche 3D-Grakengine OGRE 3D2 bietet all die Funktionalität, die man
braucht, um komplexe 3D-Grak zum Leben zu erwecken und bietet alle Funktionali-
täten, um die Möglichkeiten moderner Grakprozessoren für seine Anwendung zu nut-
zen.

Diese Arbeit beleuchtet den Funktionsumfang und die Leistungsfähigkeit der Grak-
Engine OGRE 3D, im folgenden nur mit OGRE bezeichnet. Nach einem Vergleich mit
weiteren 3D-Engines, sowohl freien als auch kommerziellen, und der Auistung der Leis-
tungsmerkmale von OGRE erfolgt eine Einführung in die Programmierung mit OGRE.
Darauf folgend werden wichtige Bereiche der Engine, wie z.B. das Materialsystem und
Animationen, näher beleuchtet und mit Beispielen verständlicht. Abschlieÿend werden
einige Werkzeuge betrachtet, die direkt mit OGRE arbeiten oder Daten mit der Engine
austauschen können.

1 API - Application Programming Interface, dt.: Schnittstelle zur Anwendungsprogrammierung


2 OGRE - Object-Oriented Rendering Engine

7
1.1 Einführung 3D-Engines

1.1.1 Begrisklärung 3D-Engine

Eine 3D-Engine 3
ist ein Kernbestandteil von Computerspielen oder anderen interaktiven
Anwendungen mit in Echtzeit berechneten Graken. Synonyme sind z.B. Grak-Engine,
Rendering-Engine oder fälschlicherweise Spiel-Engine
, deren Begri eigentlich eine höhe-
re Funktionalität (z.B. Physik und Kollisionserkennung, Sound, Scripting und Künstliche
Intelligenz) als der der 3D-Engines assoziiert. 3D-Engines kümmern sich um die Darstel-
lung möglichst realitätsnaher 3D-Grak. Sie verfügen meist über einen Szenengraphen,
der eine objektorientierte Repräsentation der 3D-Welt darstellt, das Design der Anwen-
dung vereinfacht und die Grakberechnungen von komplexen 3D-Welten optimiert.

1.1.2 Begriserklärung Hardware-Abstraktion

Eine Hardware-Abstraktionsschicht4 ist eine in Software implementierte Abstraktions-


schicht zwischen der physischen Hardware und der Software, die auf dem System läuft.
Diese Schicht versteckt die Unterschiede von verschiedener Hardware (z.B. von ver-
schiedenen Grakkarten) vor dem Anwendungsprogrammierer, der mit einem einheit-
lichen Befehlssatz möglichst jede verfügbare Hardware ansprechen will. 3D-Engines set-
zen meistens auf Grak-APIs wie Direct3D oder OpenGL auf, die eine Schnittstelle zur
Grakhardware zur Verfügung stellen und die Hardware-Abstraktionsschicht direkt an-
sprechen. Das OGRE-Rendersystem benutzt entweder Direct3D (eine gute Einführung
ist in [Sch03] zu nden) oder OpenGL. Ein gewisses Grundverständnis dieser Grak-
APIs ist sehr vorteilhaft für die Arbeit mit OGRE, weshalb hier auf die jeweiligen In-
ternetseiten http://www.microsoft.com/directx/ und http://www.opengl.org ver-
wiesen wird.

1.1.3 Geschichtliches

Der Begri 3D-Engine erschien erstmals Mitte der 90er Jahre im Zusammenhang mit
3D-Spielen. Sogenannte First-Person Shooter 5 (FPS) wie Doom oder Quake von
id Software waren die ersten sehr populären 3D-Spiele. Um nicht alles von Grund auf
neuzuprogrammieren, lizenzierten Spieleentwickler die Kernkomponenten der Software

3 wörtlich: 3D-Motor oder Grakmotor


4 HAL - engl.: Hardware Abstraction Layer
5 Ballerspiele, die aus der Ich-Perspektive gespielt werden

8
und entwickelten dazu eigene Graken, Charaktere und Welten, also Spielinhalte6 .
Spätere Spiele wie Quake III oder Unreal wurden bereits mit dem Hintergedanken
entwickelt, die 3D-Engine später an weitere Entwicklungsstudios verkaufen zu können,
was zur Folge hatte, dass die Engine und die Inhalte separat entwickelt wurden. Die
Lizenzierung der 3D-Engines wurde eine weitere Einnahmequelle für Spieleentwickler.
Jedoch beinhalten die lizenzierten Engines meist weitere Funktionalitäten auÿer der
Grakerzeugung, denn es handelt sich meistens um komplette Spiel-Engines. Auÿerdem
machen wiederverwendbare Engines die Programmierung von Spielfortsetzungen leichter
und kosteneektiver.

Auch vor 1990 gab es schon so etwas wie Spiel- oder Grak-Engines wie z.B. Lucas
Arts SCUMM-System und Incentive Softwares Freescape-Engine. Im Gegensatz
zu modernen Engines wurden diese Systeme nie von anderen Entwicklern lizenziert.
[Wik06], Suchbegri game engine

1.1.4 Unterschiede zwischen freien und kommerziellen


3D-Engines

Der gröÿte Unterschied von freien und kommerziellen 3D-Engines ist die Verfügbar-
keit und Stabilität von Werkzeugen, die Inhalte nahtlos aus 7
DCC
-Anwendungen in
die Engine importieren, möglichst Echtzeit-Feedback bieten und die Benutzung auch
von Nichtprogrammierern bewältigt werden kann. Kommerzielle Engines bieten meist
ein reichhaltiges Angebot solcher Werkzeuge. Im Gegensatz zu den freien Engines, die
meist nur die Engine an sich bieten. Weitere Werkzeuge werden meist aus Eigenini-
tiative einzelner Enthusiasten heraus programmiert mit teilweise durchaus beachtlichen
Ergebnissen. Trotzdem erreichen diese Werkzeuge meist nie den Funktionsumfang und
vorallem die Stabilität kommerzieller Produkte. Beim Importieren von Inhalten ist bei
einer freien Engine meist mehr Handarbeit zu verrichten.

Zu den meisten kommerziellen Engines werden keine oziellen Angaben zum Preis für
die Lizenzierung gemacht. Entwickeler, die die Engines benutzen, verpichten sich in
einem NDA8 darüber keine Auskunft zu geben. Man geht aber davon aus, dass High-
End Engines wie z.B. die Unreal Engine 3(siehe auch Kapitel 1.3.3 auf Seite 17) für
etwa 500.000 Dollar zu lizenzieren sind und somit wirklich nur für einige sehr groÿe
Enwticklungsstudios mit einem Budget von mehreren Millionen Dollar pro Spiel in Frage
kommen. Deshalb gibt es für Hobby- und kleinere Entwickler oder für den universitären
Bereich nur die Alternative der freien Engines. Die einzige kommerzielle Engine, die
preislich auch für diese Zielgruppe in Frage kommt, ist die Torque Game Engine
(siehe

6 engl.:
game assets
7 DCC - digital content creation, dt.: Erzeugung digitaler Inhalte
8 NDA - Non-Disclosure-Agreement, dt.: Vertraulichkeitsvereinbarung

9
auch Kapitel 1.3.1 auf Seite 15) mit einem Preis von 100 US$ pro Arbeitsplatz.

Die hier vorgestellten kommerziellen Engines sind eigentlich komplette Spiele-Engines,


d.h. sie bieten neben der Darstellung von Grak weitere Funktionen an, die man für
Spiele und interaktive Anwendungen benötigt. Dazu zählen z.B. Sound-, Netzwerk- und
Physik-Fuktionalität und Systeme für künstliche Intelligenz sowie entsprechende Werk-
zeuge für diese Bereiche. Abbildung 1.1 auf Seite 10 verdeutlicht die Komponenten einer
Spiel-Engine und deren Interaktion untereinander. Auf den folgenden Seiten werden nur
die Renderfunktionalitäten der Engines verglichen.

Abbildung 1.1: Die Komponenten einer Spiel-Engine (in Anlehnung an [Mot03], Seite
495)

1.2 Freie 3D-Engines

1.2.1 Irrlicht

Irrlicht
ist eine Multiplattform OpenSource 3D-Engine, die in C++ geschrieben wur-
de aber auch für .NET Sprachen erhältlich ist9 . Sie benutzt entweder OpenGL oder

9 kostenlos erhältlich unter: http://irrlicht.sourceforge.net/

10
die DirectX-API. Irrlicht unterstützt PerVertex-
10
und PerPixel-Lighting Lightmap-
11
,
ping Bump-
12
, Normal-Mapping
und 13
sowohl durch die Fixed-Function-Pipeline14
als
Shader
auch durch 15
bis Shadermodell 3.0 in HLSL
16
und GLSL
17
. Ein hierarchicher
Szenenmanager ermöglicht die Verwaltung der Szene in Octrees
18
als auch durch Oc-
clusion Culling19
. 3D-Modelle können aus diversen 3D-Modelling-Anwendungen wie 3ds
Max, Blender, Milkshape und Maya importiert werden. Die Animation von 3D-Modellen
erfolgt über Morphing und skeletale Animation (siehe Kapitel 6 auf Seite 67). Die Engine
stellt auÿerdem ein Lua-Scripting-Interface bereit. [Dev06]

irrEdit ist ein frei erhältlicher Szeneneditor für Irrlicht, der sich allerdings noch in der
Entwicklung bendet und bisher erst in Version 0.4 vorliegt20 .

Für Irrlicht sind zahlreiche Tutorials und eine gute Dokumentation verfügbar, die den
Einstieg in die Irrlicht-Programmierung sehr erleichtert. Auÿerdem beschäftigt sich eine
rege Community mit Irrlicht.

Kommerzielle Projekte, die mit Irrlicht realisiert wurden, sind nicht bekannt. [Dev06]
und [Irr06]

1.2.2 Nebula Device 2

Nebula Device 2 ist eine OpenSource 3D-Engine in C++ von Radon Labs21 aus Berlin.
Sie ist eine moderne Engine, die ihre komplette Rendering-Pipeline durch Shader reali-
siert und dafür DirectX verwendet. Bisher kann man sie nur unter Windows verwenden,
Portierungen nach Linux und Max OSX sind in Planung. Sie besitzt Skriptinterfaces für
Python, Lua und Ruby.

Es gibt verschiedene Szenenmanager für unterschiedliche Anforderungen wie Portale,


Octrees und Occlusion Culling. 3D-Modelle können aus dem Wavefront und dem Lega-
10 Lichtberechnungen werden pro Vertex durchgeführt und die Ergebnisse werden zwischen zwei Vertizes
interpoliert
11 Lichtberechnungen werden für jeden Pixel einzeln durchgeführt was realistischere Ergebnisse als
PerVertex-Lighting ermöglicht
12 Lichtberechnungen werden vorberechnet und direkt in Texturen geschrieben. Dies ermöglicht nur
statische Beleuchtung
13 Hinzufügen von Oberächendetails durch spezielle Texturen
14 bisherige, nicht-programmierbare Funktionen der Grakkarte
15 programmierbare Prozessoren auf modernen Grakkarten
16 HLSL - High Level Shading Language, Programmiersprache für Shader von Microsoft
17 GLSL - OpenGL Shading Language, Programmiersprache für Shader, für Informationen siehe [Ros04]
18 Algorithmus zur Aufteilung des Raumes in immer kleiner werdende Würfel
19 Algorithmus, um verdeckte Bereiche der Szene von der Grakberechnung auszuschlieÿen
20 erhältlich unter: http://irredit.irrlicht3d.org/
21 erhältlich unter: http://www.radonlabs.de

11
cy n3d-Format importiert werden. Exporter für Maya und 3ds Max sind kommerziell
erhältlich. Die Engine unterstützt Keyframe- und skeletale Animation, Morphing und
Animationsüberblendung. Das Rendering erfolgt über Shader bis Shadermodell 3.0. Par-
tikelsysteme werden ebenfalls unterstützt.

Die Engine wurde bereits in vielen kom-


merziellen Produkten wie z.B. verschie-
denen Lernspielen des Cornelsen Verlags
(z.B. Task Force Biologie), in Project
Nomads und in Verliebt in Berlin ein-
gesetzt. Das Rollenspiel Drakensang be-
ndet sich momentan bei Radon Labs in
Entwicklung (siehe Abbildung 1.2 auf Sei-
te 12). [Dev06] und [Rad06]

1.2.3 Panda3D
Abbildung 1.2: Screenshot von dem noch
nicht veröentlichten Nebu- Panda3D
22
ist eine 3D-Engine für SGI,
la Device Spiel Drakensang Linux, Sun und Windows. Sie wurde ur-
sprünglich von Walt Disney Imagineering
VR Studio für das Online-Multiplayerspiel
Toontown Online (siehe Abbildung 1.3
auf Seite 13) entwickelt. Die Engine ist in
C++ geschrieben und verfügt über ein Python Skriptinterface. Panda3D ist jetzt als
Open Source Software unter http://www.panda3d.org verfügbar.

Das Rendering erfolgt wahlweise über OpenGL oder DirectX. Ein Python Kommando-
zeilenfenster ermöglicht die Änderung von Python Skripten während der Ausführung
der Panda3D-Anwendung. 3D-Modelle können aus 3ds Max und Maya in das Panda3D-
eigene Egg-Format exportiert werden. Die Szeneninhalte werden über einen Szenengra-
phen verwaltet und mit einem Szeneneditor platziert. Die Engine unterstützt Vertexbe-
leuchtung, animierte Texturen, Partikelsysteme und Nebel oder Shader in 23
Cg
. Anima-
tionen erfolgen durch inverse Kinematik oder skeletale Animation. Auÿerdem beinhaltet
die Engine ein einfaches Physik- und Künstliche Intelligenz-System.

Bisher kam die Engine nur in Toontown und in verschiedenen studentischen Projekten
des Carnegie Mellon Entertainment Technology Center in Pittsburgh, USA zum Einsatz.
Das liegt vielleicht daran, dass die Engine mit der vorhanden Dokumentation nur in Py-
thon programmiert werden kann und somit der Quellcode immer öentlich sichtbar und

22 Panda - Platform Agnostic Networked Display Architecture


23 Programmmiersprache für Shader von NVidia

12
veränderbar bleibt. Dies schlieÿt die Engine zur Verwendung in kommerziellen Projekten
praktisch aus. Es ist zwar möglich die Engine direkt in C++ zu programmieren, jedoch
gibt es dafür keine Dokumentation und Tutorials. [Dev06] und [Pan06]

1.2.4 Vergleichende Betrachtungen zu OGRE 3D

Abschlieÿend kann man sagen, dass al-


le hier vorgestellten freien Engines einen
ähnlich Funktionsumfang haben und auch
in mittlerweile stabilen Versionen ver-
fügbar sind. Eine vergleichende Tabelle
der Leistungsmerkmalen der Engines ist
auf Seite 14 zu nden. OGRE verfügt
im Gegensatz zu den anderen Engines
über ein leistungsfähiges Post-Processing-
Framework. Die damit erreichten Eekte,
wie z.b. HDRR und Tiefenunschärfe, wer-
den in kommenden 3D-Anwendungen im-
mer wichtiger. Auÿerdem ermöglicht OG-
Abbildung 1.3: Disneys Toontown Online
RE die komplette Ausnutzung der Fixed
Function Pipeline sowie Unterstützung für
alle Shadermodelle und -programmiersprachen. Die anderen betrachteten Engines bieten
in dieser Hinsicht nicht alle Leistungsmerkmale, die OGRE bietet.
Zum ausschlaggebenden Kriterium für ORGRE 3D als Betrachtungsobjekt dieser Ar-
beit ist für mich aber die sehr lebendige Community, mit ihren stets gefüllten Foren
und Wikis, geworden. Die Archive bieten in vielen Fällen bereits die benötigten Ant-
worten auf aufkommende Fragen und wenn nicht, dann werden diese meist in Stunden
von den kompetenten Betreuern der Foren, oft sogar vom OGRE-Schöpfer Steve Stree-
ting persönlich, beantwortet. OGRE bietet dem Einsteiger in die 3D-Programmier ein
durchdachtes und klares Klassensystem und einen leichten Einstieg durch zahlreiche Tu-
torials. Irrlicht wird dem Neuling ebenfalls durch zahlreiche Tutorials nahegebracht. Die
Nebula Device 2 Engine ist durch ihr komplett auf Shader ausgerichtetes Rendersystem
eher nicht für Einsteiger zu empfehlen und die Panda3D Engine hängt der technischen
Entwicklung etwas hinterher und es gibt nur wenige Tutorials.
Desweiteren wurde OGRE dieses Jahr erstmalig durch den Google Summer of Code
unterstützt, einer Initiative von Google zur Weiterentwicklung ausgezeichneter Open
Source Software. Die Engine hält der technischen Entwicklung von Computersystem mit
und wurde bereits in den Spielen Ankh vom deutschen Entwickler Deck13, das im Rah-
men des Deutschen Entwicklerpreis zum besten deutschen Spiel 2005 gekürt wurde, und
Pacic Storm von Buka Entertainment aus Russland auf die Nutzung in komplexen,
kommerziellen Projekten getestet.

13
Die Engine ist unter der LGPL24 lizensiert. Dies ermöglicht die freie Verwendung der
Engine sowohl in OpenSource als auch in kommerziellen Projekten. Im Gegensatz zur
GPL25 müssen Projekte nicht als OpenSource veröentlicht werden, sondern können,
soweit die Engine nicht verändert wurde, in einer eigenen Lizenz erscheinen.

Abbildung 1.4: Freie Engines im Vergleich

24 LGPL - Lesser General Public License


25 GPL - General Public License

14
1.3 Kommerzielle 3D-Engines

1.3.1 Torque Game Engine

Die Torque Game Engine 26


oder TGE ist eine modizierte Version von einer Spiel-
Engine, die ursprünglich von Dynamix für das Spiel Tribes 2 entwickelt wurde. Jetzt
ist die Torque Engine für unabhängige oder professionelle Spieleentwickler von Garage
Games linzenzierbar.
Die Torgue Indie License kostet pro Programmierer moderate 100 US$ solange der
Programmier in einer Firma mit weniger als 250.000 US$ Umsatz jährlich angestellt ist.
Die alternative kommerzielle Lizenz kostet 495 US$ pro Programmierer. Diese Preis-
politik ermöglicht es auch kleinen Entwicklungsstudios eine leistungsfähige Engine zu
benutzen.

Auÿer 3D-Grak beinhaltet die Engi-


ne auch Netzwerkunterstützung, Scrip-
ting, einen Welteditor und Benutzer-
schnittstellenerzeugung. Die Engine lässt
sich unter Windows, Macintosh und Li-
nux verwenden. Die Engine unterstützt
Landschafts-Rendering mit mehreren Tex-
turen (Multi-Texturing), volumetrischer
Nebel, Umgebungs-Mapping (Environ-
ment Mapping), Vertex-Lighting und das
Laden von 3D-Modellen im .DTS- und
.DIF-Format. .DTS-Modelle können über
skeletale oder über Morph-Animation ani-
Abbildung 1.5: Marble Blast Ultra auf der
miert werden. Auÿerdem kann stufenlos
XBox 360
zwischen mehreren Animation überblen-
det werden. .DIF-Modelle werden für die
statische Szenengeometrie benutzt, da sie nicht dynamisch beleuchtet werden können.

Die Torque Engine enthält mehrere Werkzeuge zur Inhalteerstellung, wie einen GUI-
Editor, einen Welteneditor zum Platzieren von Objekten und Modizieren von Szenen-
eigenschaften, einen Generator zur Erzeugung von Höhentexturen als Grundlage für
Gelände, einen Geländetextureditor, einen Editor zur Modizierung der Geländeform
und einen Modelleditor.

Hauptkritikpunkt der Engine ist die unzureichende Dokumentation. Garage Games hat
dieses Problem erkannt und unter http://tdn.garagegames.com das Torque Developer

26 erhältlich unter http://www.garagegames.com

15
Network ins Leben gerufen, in der Honung, dass die Benutzer der Engine über das
Wiki-System eine vollständigere Dokumentation erschaen.

Die Torque Engine kann über die Torque Shader Enginezur Unterstützung von mo-
dernen Shadern erweitert werden. Jedoch wird nur das Shadermodell 2.0 unterstützt.
Es gibt mehrere Starter Kits, die die Entwicklung bestimmter Spieletypen erleichtern
sollen, wie z.B. das Racing Starter Kit für Rennspiele.

Bekanntestes Spiel, das die Torque Engine nutzt, ist Marble Blast Ultra welches für
den PC als auch für die XBox verfügbar ist (siehe Abbildung 1.5 auf Seite 15).

GarageGames hat bereits mit Torque Xdie nächste Version der Engine angekündigt.
Torque X baut auf dem neuen XNA Framework 27
von Microsoft auf, eines Frameworks
zur Entwicklung von Spielen und anderen interaktiven Anwendungen. Torque X kann
mit dem neuen XNA Game Studio Express28
, welches auf der frei erhältlichen Entwick-
lungsumgebung Visual C# Express
29
aufsetzt, benutzt werden, um mit dem gleichen
Programmcode Anwendungen sowohl für den PC mit Windows XP als auch für Micro-
softs Spielekonsole XBox 360 zu entwickeln. [Dev06] und [Gar06]

1.3.2 CryEngine 1/2

Die CryEngine ist eine Spiel-Engine die


im Spiel Far Cry von Crytek30 benutzt
wurde. Ursprünglich wurde die Engine für
eine Technologie-Demonstration für NVi-
dia geschrieben.

Die CryEngine2 ist eine neue in der


Entwicklung bendliche Spiel-Engine, die
stark auf der CryEngine basiert und für
den kommenden Crytek-Titel Crysis ge-
Abbildung 1.6: Screenshot aus einer Crysis- nutzt wird. Bisher gab es in der Öentlich-
Demonstration keit nur eine beeindruckende technische
Demonstration zu sehen31 . Die Demo zeig-
te fortgeschrittene Technologien wie z.B.
dynamisches indirektes Licht (Dynamic Ambient Lighting), weiche Schatten, Tiefen-

27 Informationen und kostenloser Download unter: http://msdn.microsoft.com/directx/xna/


28 kostenlos erhältlich unter http://msdn.com/directx/XNA/gse/
29 erhältlich unter http://msdn.microsoft.com/vstudio/express/visualcsharp/
30 eher spärliche Informationen zur Engine unter: http://www.crytek.com
31 erhältlich unter http://www.youtube.com/watch?v=MqSOZGCcFYU

16
sowie Bewegungsunschärfe und extrem detaillierte 3D-Modelle. Erste persönliche Ein-
drucke der Engine von einer Multiplayer-Demonstration auf der Games Convention 2006
in Leipzig belegen die Möglichkeit dieser Technologien in Echtzeit (siehe Abbildung 1.6
auf Seite 16). [Dev06]

1.3.3 Unreal-Engine 2/3

Die Unreal-Engine32
ist eine der popu-
lärsten Spiele-Engines und sie kam in der
ersten Version 1998 mit dem gleichnami-
gen Spiel heraus und wird von Epic Ga-
mes entwickelt. Die Engine unterscheidet
sich zu den meisten anderen Engines dar-
in, dass man mit der Lizenz nicht nur
die Engine sondern gleich ein komplettes
Spiel mit Quellcode und allen Inhalten er-
wirbt. Das hat den Vorteil dass man sehr
schnell einen eigenen Prototyp entwickeln
kann, aber den Nachteil, dass je mehr sich
das zu entwickelnde Spiel von einem klas-
(a) Hochaufgelöstes (b) Niedrigaufgelöstes sischen First Person Shooter (FPS) un-
Modell bestehend aus Modell bestehend aus
2 Mio. Dreiecken ca. 5000 Dreiecken terscheidet, desto schwieriger es wird die
Engine den eigenen Bedürfnissen anzu-
passen. Die Unreal-Engine kann über die
UnrealScript-Skriptsprache gesteuert wer-
den.

Die Unreal-Engine 2 baut auf Direct3D


8 und OpenGL 1.x auf und unterstützt
bereits Shadermodell 1. Sie schat naht-
lose Übergänge zwischen Indoor- 33
- BSP
Szenen, statischen und dynamischen Mo-
dellen und Outdoor-Szenen. Sie unter-
stützt skeletale Animation und Gesichtsa-
nimation inkklusive Lippensynchronisati-
(c) Endergebniss in der Engine: niedrigaufge- on.
löstes Modell mit Normal Map Die Engine hebt sich durch ihr reichhal-
tiges Angebot an funktionsstarken Werk-
Abbildung 1.7: Normal Mapping in der Un- zeugen hervor. So bieten der Level-Editor
real Engine 3
32 nähere Informationen unter: http://www.unrealtechnology.com
33 BSP - Binary Space Partition, Algorithmus zur Aufteilung des Raumes in eine baumartige Struktur

17
UnrealED alles um einen Spiellevel zu erstellen, inklusive der Festlegung der Spiellogik
über Skripte. Es gibt weitere Editoren für Partikelsysteme, Benutzerinterfaces, Zwischen-
sequenzen in Spielgrak (sogenannte cutscenes) und für Modellanimationen. UnrealED
ist z.B. in dem Spiel Unreal Tournament 2004 enthalten und ermöglicht es, neue Karten
für das Spiel zu erstellen.
Die Version 2.5 erweitert die Engine um Direct3D 9 und OpenGL 2.x Unterstützung.
Somit werden auch moderne Graktechnologien wie HDRR34 , Normal und Bump Map-
ping, weiche Schatten und Per Pixel Lighting möglich. Nun werden auch Microsofts
XBox, 64-Bit Windows und Linux von der Engine unterstützt.

Die Unreal-Engine 3 bendet sich noch in der Entwicklung und wird für PCs, Micro-
softs XBox 360 und Sonys Playstation 3 erältlich sein. Die Engine läuft mit Direct3D
9 und 1035 und OpenGL 2.x. Sie unterstützt Shadermodell 2, 3 und 4 inklusive der neu-
en Geometry Shader 36
und verfügt über eine komplette 64- oder 128-Bit High Dynamic
Range Rendering (HDRR) - Pipeline.
Die Editoren werden weiterhin in ihrer Funktionalität ausgebaut. UnrealCascade ermög-
licht das Editieren komplexer Partikelsystem, mit UnrealPhAT editiert man die Skelette
und physikalischen Eigenschaften der 3D-Modelle, AnimTree erlaubt das Editieren und
Mischen mehrerer einzelner Animationen zu komplexen Bewegungen, ein visueller Ma-
terialeditor erlaubt die Erstellung komplexer Materialien ohne direkt Shader program-
mieren zu müssen und UnrealED, der leistungsstarke Level-Editor, der alle anderen
Werkzeugen in einer Oberäche vereint.
Die Engine verfügt über ein Werkzeug zur automatischen Erstellung von Normal Maps.
Dabei wird aus einem extrem hoch aufgelösten (bis 8 Mio. Dreiecke) Modell eines Cha-
rakters eine hochaufgelöste Normal Map (2048 x 2048 Pixel) erstellt und in der Engine
mit einem niedrig aufgelösten (bis 12000 Dreiecke) Mesh gerendert (siehe Abbildung 1.7
auf Seite 17). [Wik06], Suchbegri unreal engine und [Unr06]

34 HDRR - High Dynamic Range Rendering, Licht wird mit einer höheren Werteumfang berechnet, was
z.B. Überstrahleekte ermöglicht
35 Direct3D 10 wird erst mit der Veröentlichung von Windows Vista anfang 2007 erhältlich sein
36 neue Stufe im Renderingprozess, zwischen Vertex- und Pixel-Shader angeordnet, ermöglicht er z.B.
die prozedurale Erzeugung von 3D-Geometrie direkt auf der Grakkarte

18
1.3.4 Source-Engine

Die Source Engine wurde bis 2005 von Valve Software für das Spiel Half-Life 2 entwi-
ckelt. Sie läuft unter Direct3D 9 und unterstützt HLSL bis Shadermodell 2.0, ist aber bis
zu Direct3D 6 abwärtskompatibel mit älterer Grakhardware. Zu den Leistungsmerkma-
len zählen weiterhin Bump und Environment Mapping, dynamische Lichter, projektive
Schatten und vorberechnete Light Maps, HDR-Beleuchtung, Wasser mit Brechungseek-
ten und komplexe Partikelsysteme. Valve hebt besonders die Charakteranimationsmög-
lichkeiten der Engine hervor. So werden Charaktere über ein skeletales System animiert.
Im Gesichtsbereich kommt ein simuliertes Muskelsystem zum Einsatz, das die Charaktere
lebensnaher erscheinen lassen soll (siehe Abbildung 1.8 auf Seite 19). Auch die Lippen
der Charaktere können zumindest grob auf mehrere Sprachen synchronisiert werden.

Die Engine ist in C++ geschrieben und


ist nur für den PC erhältlich. Sie ist über
DLLs modular aufgebaut. Zu den um-
fangreichen Werkzeugen zählt der Valve
Hammer Editor, der ähnlich dem Unreal-
ED eine komplette Umgebung zur Er-
stellung von Spielszenen mit sofortigen
optischen Feedback zur Verfügung stellt.
Der Half-Life Model Viewer erlaubt die
Betrachtung und Bearbeitung einzelner
3D-Modelle. Über den Faceposer werden
sämtliche Gesichtsanimationen der Cha-
raktere erstellt. Auÿerdem verfügt die En-
Abbildung 1.8: Lebensecht wirkende Cha-
gine über Exporter für 3ds Max, Maya und
raktere im Spiel Half-Life 2
Softimage XSI. [Val06]

19
2 Leistungsumfang von OGRE 3D

OGRE ist eine szenenorientierte, exible in C++ geschriebene 3D-Engine ,die eine ein-
fache und intuitive Möglichkeit zur Nutzung moderner Grakhardware zur Verfügung
stellt. Die Klassenbibliothek abstrahiert alle Details der darunterliegenden Systembiblio-
theken wie Direct3D oder OpenGL und ist damit wirklich plattformunabhängig.

2.1 Generelle Leistungsmerkmale

• OGRE ist komplett objekt-orientiert gestaltet und bietet eine einfache Schnittstelle
zum Rendering von dreidimensionalen Szenen unabhängig von der verwendeten
Grak-API-Implementierung.
• Eine exible Plugin-Architektur ermöglicht die Erweiterung der Engine ohne er-
neute Kompilierung.
• Konsistentes Design und Dokumentation aller Klassen.
• Unterstützung von ZIP-Archiven.

2.2 Unterstützte Plattformen und 3D-APIs

• Als 3D-APIs werden OpenGL und Direct3D unterstützt.


• Windows-, Linux- und MAC OSX-Unterstützung, experimentelle PocketPC1 (Win-
dows Mobile) Unterstützung.
• Kann unter Windows mithilfe von Visual C++2 (Visual C++ 6 bis OGRE Version
1.0.7, Visual Studio 2003 und 2005 ab OGRE Version 1.2) und Code::Blocks erstellt
werden.
• Kann unter Linux und MAC OSX mithilfe von gcc 3+3 erstellt werden.
1 Informationen zur Implementierung unter: http://www.ogre3d.org/phpBB2/viewtopic.php?t=
15938&highlight=
2 Visual C++ Express ist kostenlos erhältlich unter: http://msdn.microsoft.com/vstudio/express/
visualc/
3 erhältlich unter: http://gcc.gnu.org/releases.html

20
2.3 Material- und Shader-Unterstützung

• Eine mächtige Material-Skriptsprache ermöglicht die Ausarbeitung und Speiche-


rung von Materialdenitionen auÿerhalb der eigentlichen Anwendung in Skriptda-
teien.
• Unterstützung aller Funktionen der Fixed Function Pipeline wie z.B. Multitexture-
Rendering (mehrere Texturen werden ineinandergeblendet und auf eine 3D-Geometrie
gerendert), Multipass-Blending (z.B. für durchsichtige Oberächen), Texturkoor-
dinatengenerierung und -modikation und Nebeleekte für ältere Grakhardware.
• Moderne Vertex- und Pixel-Shader, entweder in Assembler oder in Hochsprachen
wie HLSL, Cg und GLSL geschrieben, werden unterstützt.
• Multipass Rendering sorgt z.B. für die Beleuchtung eines Objekts mit mehreren
Lichtquellen.
• Mehrere Techniken pro Materialdeniton erlauben verschiedene Implementationen
von Eekten für verschieden leistungsfähige Grakkarten. OGRE erkennt welche
Technik verwendet werden kann und benutzt die Beste.
• Gleiches gilt für Material-LODs 4 : Mit zunehmender Entfernung der Objekte zum
Betrachter kann ein weniger rechenintensives Material benutzt werden, um die
Performance zu steigern.
• Texturen können in folgenden Formaten geladen werden: PNG, JPEG, TGA, BMP
und DDS, wobei auch ungewöhnliche Texturformen wie volumetrische und kubi-
sche (z.B. für Umgebungs-Mapping) sowie komprimierte (DXT/S3TC) Texturen
unterstützt werden.
• Texturen können in Echtzeit zur Verfügung gestellt werden, z.b. für Live-Video-
Streams.
• Mip-Mapping sorgt dafür, dass weit entfernte Objekte eine verkleinerte Version
der ursprünglichen Textur verwenden. Dabei werden die einzelnen Mip-Maps au-
tomatisch von OGRE erzeugt sollten sie nicht bereits in der Texturdatei vorhanden
sein.

2.4 3D-Geometrien

• Mehrere Exporter für 3D-Modelling-Anwendungen, wie z.B. 3DS Max, Maya, Blen-
der und Milkshape3D, exportieren in das OGRE-eigene Mesh-Format .mesh (siehe
Kapitel 7.5, Seite 76).
• Manuell oder automatisch erzeugte Mesh-LODs sorgen für eine erhöhte Perfor-
mance.
4 LOD - Level of Detail, dt.: Detailgrad

21
2.5 Animation

• Skeletale Animation mit gewichtsgesteurter Überblendung mehrerer Animationen.


Knochengewichte können manuell eingestellt werden und Knochen auch manuell
animiert werden. Kongurierbare Interpolationsmethoden ermöglichen die Wahl
zwischen genauer und langsamer und ungenauerer aber schnellerer Interpolation.
• Morph-Animation zur linearen Überblendung zweier Formen.
• Pose-Animation erlaubt die Überblendung mehrerer Formen mit steuerbaren Ge-
wichten über die Zeit hinweg für z.B. Gesichtsanimationen.
• Generische Animationsspuren ermöglichen die Animation jedes Parameters über
die Zeit hinweg.

2.6 Szenenverwaltung

• Das integrierte Szenenmanagement ist sehr exibel gestaltet und nicht an einen
bestimmten Szenentyp gebunden. Man kann vordenierte Klassen benutzen, wie
z.B. BSP oder Octree, oder eigene Unterklassen schreiben, um volle Kontrolle
über die Szenenverwaltung zu bekommen und diese an die spezielle Anwendung
anpassen.
• Ein hierarchischer Szenengraph verwaltet alle szenenrelevanten Objekte und sorgt
für eine inhaltliche wie räumliche Beziehung der Objekte untereinander.

Für weitergehende Information zur Szenenverwaltung von interaktiven Anwendungen im


Allgemeinen siehe [Eri05].

2.7 Spezialeekte

• Das Compositor-System erlaubt die einfache Verwendung von Post-Processing


Eekten, wie z.B. Bewegungsunschärfe und verschiedene Filtereekte über eine
Skriptsprache ähnlich den Materialskripten.
• Ein skriptbasiertes Partikelsystem ermöglicht Partikelemitter und -aektoren.
• Unterstützung von Himmelsboxen (6 Texturen bilden die Umgebung der Szene),
Himmelsebenen und Himmelshalbkugeln (jeweils eine Textur) zur Ausgestaltung
der Szene.
• Billboards5 als Platzhalter für zweidimensionale Sprites, z.B. für weit entfernte
Bäume oder Wolken.
5 Rechtecke, die immer dem Betrachter zugewandt sind und auf die eine Textur mit Alpha-Kanal
projiziert wird

22
• Transparente Objekte werden automatisch verwaltet. Man muss sich nicht um die
Renderreihenfolge kümmern.
• Verschiedene Schatten-Render-Methoden wie modulative, additive, stencil- und
texturbasierte Schatten werden unterstützt und werden wenn möglich direkt auf
der Grakhardware berechnet.

2.8 Weitere Leistungsmerkmale

• Einheitliches Ressourcen- und Speichermanagement.


• Ein Debugger für den Speichermanager hilft beim Finden von Speicherlöchern.
• Sogenannte Controller erlauben die Verbindung bestimmter Parameter unterein-
ander.
• Der ReferenceAppLayer stellt ein Beispiel zur Verfügung, wie man OGRE mit an-
deren Bibliotheken, wie z.B. ODE6 für Kollisionserkennung und Physik, verbindet.
• TrueType Fonts können zur Ausgabe auf dem Bildschirm genutzt werden.
• Die 2D-GUI7 CEGUI (Crazy Edies GUI) ist bereits in OGRE integriert und
bietet Buttons, Listen Editierboxen, Scrollbalken usw.

[The06b] und [Dev06]

6 ODE - Open Dynamics Engine


7 GUI - Graphical User Interface, dt.: grasche Benutzerschnittstelle

23
3 Einführung in die OGRE
3D-Programmierung

3.1 Kernobjekte

Die in Abbildung 3.1 auf Seite 24 dargestellten Objekte bilden die Basis für jede OGRE-
Anwendung:

Abbildung 3.1: Die OGRE-Kernobjekte und ihre Beziehungen untereinander (in Anleh-
nung an [The06b])

24
3.1.1 Root-Objekt

Das Root -Objekt stellt den Einstiegspunkt in das OGRE-System dar. Dieses Objekt
muss als allererstes OGRE-Objekt erstellt werden und als letztes zerstört werden.

Über das Root-Objekt lässt sich das Rendersystem kongurieren. Die Methode show-
ConfigDialog() prüft alle Renderoptionen, wie verfügbare Renderer (OpenGL oder Di-
rectX), Auösungen, Farbtiefen, Vollbildschirm- und Antialiasing-Optionen. Auÿerdem
speichert die Methode die vorgenommenen Einstellungen in einer Kongurationsdatei
zur späteren Initialisierung des Render-Systems.

Das Root-Objekt stellt Zugri zu wichtigen Objekten des OGRE-Systems, wie dem
Szenen-, dem Mesh- und Texturmanager, bereit.

Die Anwendung betritt durch den Aufruf der Root-Objekt-Methode startRendering()


die Renderschleife, die nur durch die Schlieÿung aller Renderfenser oder durch eine ex-
plizite angeforderte Beendigung der Anwendung aus einem FrameListener (siehe Kapitel
3.4 auf Seite 37) heraus beendet werden kann.

3.1.2 RenderSytem-Objekt

Das RenderSystem -Objekt ist eine abstrakte Klasse, die das Interface zur darunter-
liegenden Render-API deniert. Es ist für das Senden der Renderoperationen an die
verwendete API und für das Setzen aller Renderoptionen verantwortlich. Die Klasse ist
abstrakt, weil die komplette Implementation spezisch für die jeweilige Render-API ist.
Es gibt API-spezische Unterklassen für jede Render-API, z.B. D3DRenderSystem für
Direct3D. Nachdem das System durch Root::initialise() initialisiert wurde, ist das
RenderSystem-Objekt für die gewählte Render-API über die Root::getRenderSystem()-
Methode verfügbar.

Normalerweise ist es nicht nötig direkt auf das RenderSystem-Objekt zuzugreifen. Al-
les was zum Rendern von Objekten und dem Einstellen der Renderoptionen benötigt
wird, ist über den Szenenmanager sowie über Materialien und anderen szenenorientier-
ten Klassen verfügbar. Der Szenenmanager sorgt dafür, dass die benötigte Methoden des
RenderSystem-Objekt zum richtigen Zeitpunkt aufgerufen werden.
Man benötigt das RenderSystem-Objekt jedoch um mehrere separate Renderfenster zu
erstellen oder um andere fortgeschrittene Möglichkeiten des Rendersystems zu nutzen.

25
3.1.3 SceneManager-Objekt

Neben dem Root-Objekt ist das SceneManager -Objekt der wichtigste Teil des OGRE-
Systems für den Anwendungsprogrammierer. Der Szenenmanager kennt die Inhalte der
Szene, die gerendert werden sollen. Er ist verantwortlich für die Organisation der Inhalte,
wie Kameras, bewegliche Objekte (Entities, siehe Kapitel 3.1.5 auf Seite 28), Lichtquellen
und Materialien (Oberächeneigenschaften von Objekten) sowie für das Verwalten der
Weltgeometrie, die eine statische Geometrie zur Repräsentation der unbeweglichen Teile
der Szene darstellt.

Es ist nicht nötig, in der Anwendung Listen von Objektverweisen zu führen. Der Sze-
nenmanager führt alle erstellten Objekte durch zugewiesene eindeutige Namen. Der
Zugang zu Objekten geschieht durch verschiedene SceneManager-Methoden wie z.B.
getCamera() und getEntity(), und dem eindeutigen Namen.

Der Szenenmanager sendet die Szene an das RenderSystem-Objekt wenn die Szene ge-
rendert werden soll.

Die meiste Interaktion mit dem Szenenmanager geschieht während der Erstellung der
Szene. SceneManager-Methodenaufrufe sorgen dafür, dass sich die Szene mit Inhalten
füllt. Es werden 3D-Geometrien geladen, mit Materialien versehen, in der Szene platziert
sowie Kameras und Lichtquellen deniert. Die spätere (dynamische) Veränderung der
Szene wird über FrameListener-Objekte (siehe Kapitel 3.4 auf Seite 37) vollzogen.

Weil verschiedene Szenentypen (z.B. Outdoor / Indoor) verschiedenen Algorithmen be-


nötigen, um zu entscheiden, welche Objekte zum Rendern an das Rendersytem gesendet
werden, um eine möglichst gute Performance zu erzielen, gibt es mehrere Unterklassen
der SceneManager-Klasse. Der Standard-Szenenmanger rendert eine Szene ohne Organi-
sation der Objekte und man sollte keine gute Performance bei groÿen Szenen mit vielen
Objekten erwarten. Spezialisierungen der SceneManager-Klasse werden für bestimmte
Szenentypen geschaen, um die Szenenorganisation für diesen Typ zu optimieren und
eine gute Performance zu erzielen. Zum Beispiel optimiert der BspSceneManager das
Rendern von groÿen Indoor-Levels basierend auf Binary Space Partition Trees.

Die OGRE-Anwendung muss nicht wissen welche Unterklassen verfügbar sind. Die An-
wendung ruft nur Root::createSceneManager() mit dem Typ der Szene als Parameter
auf (z.B. ST_GENERIC, ST_INTERIOR) und das OGRE-System nutzt automatisch
die beste verfügbare SceneManager-Unterklasse oder den Standard-Szenemanager wenn
keine Spezialisierung verfügbar ist. Dies ermöglicht es erst später einen optimierten Sze-
nenmanager zu erstellen.

26
3.1.4 ResourceManager-Objekt

DieResourceManager -Klasse ist eine Basisklasse weitere Klassen, die zum Verwalten von
Ressourcen benutzt werden. Ressourcen sind Daten, die geladen werden müssen wie z.B.
Texturen und 3D-Geometrien (Meshes). Es gibt Unterklassen von der ResourceManager-
Klasse, um jeden Ressourcentyp zu verwalten, wie z.B. den TextureManager um Textu-
ren zu laden und den MeshManager um 3D-Geometrien zu laden.

Ressourcemanager sorgen dafür, dass Ressourcen nur einmal geladen werden und verwal-
ten die benötigten Speicherbereiche. Auÿerdem werden die angeforderten Ressourcen in
mehreren zuvor spezizierten Verzeichnissen oder komprimierten Archiven (zip-Dateien)
gesucht.

Normalerweise interagiert man nicht direkt mit den Ressourcenmanagern. Ressourcen-


manager werden von anderen OGRE-Teilen aufgerufen wenn sie benötigt werden. Wenn
man z.B. eine Textur einem Material hinzufügen will, wird der TextureManager auto-
matisch aufgerufen. Man kann aber auch direkt Ressourcen über die Ressourcenmanager
vorladen, um einen späteren Festplattenzugri innerhalb der Rendering-Schleife zu ver-
meiden.

Man muss jedoch den Ressourcenmanager direkt aufrufen, um ihm mitzuteilen wo er


nach Ressourcen suchen soll. Dies geschieht über addSearchPath() und addArchive().
Dadurch werden aber nur die Suchverzeichnisse und -archive des speziellen Ressourcen-
manager gesetzt, z.B. hat es nur Auswirkung auf das Laden von Texturen wenn man die
Methoden des TextureManagers aufruft. Alternativ kann man die statische Methode
ResourceManager::addCommonSearchPath() oder ResourceManager::addCommonAr-
chive() aufrufen, um alle Ressourcenmanager auf die Verzeichnisse und Archive zu-
greifen zu lassen.

3.1.5 Mesh-Objekt

Ein Mesh -Objekt repräsentiert die 3D-Geometrie eines Modells und ist normalerwei-
se relativ klein gegenüber der Weltgeometrie. Mesh-Objekte repräsentieren bewegliche
Objekte und werden nicht zur Repräsentation der Weltgeometrie genutzt.

Mesh-Objekte sind eine Art von Ressourcen und werden durch den MeshManager Res-
sourcenmanager verwaltet. Sie werden normalerweise aus dem OGRE-spezischen .mesh-
Format geladen. Mesh-Dateien werden meist aus 3D-Modelling-Anwendungen wie z.B.
3ds Max (siehe Kapitel 7.5.1 auf Seite 77) exportiert.

Man kann Mesh-Objekte auch manuell erzeugen, indem man die MeshManager::create-

27
Manual()-Methode aufruft. Auÿerdem gibt es Methoden, um z.B. Ebenen generisch zu
erzeugen (siehe Kapitel 5.2.1 auf Seite 65).

Mesh-Objekte bilden die Basis für individuelle bewegliche Objekt in der Szene, wel-
che Entities (siehe Kapitel 3.1.5 auf Seite 28) genannt werden. Mesh-Objekte können
auÿerdem über skeletale Animation (siehe Kapitel 5.3 auf Seite 67) animiert werden.

Mesh-Objekte setzen sich aus einem oder mehreren SubMesh -Objekten zusammen. Jedes
repräsentiert einen Teil des Meshes, der ein individuelles Material benutzt. Wenn ein
Mesh nur ein Material benutzt, so sollte es auch nur aus einem SubMesh bestehen.

3.1.6 Entity-Objekt

Ein Entity ist eine Instanz eines beweglichen Objektes in der Szene. Es kann alles Mög-
liche darstellen. Es wird nur die Annahme gemacht, dass es keine feste Position in der
Szene hat.

Entities basieren auf 3D-Geometrien, die durch das Mesh-Objekt repräsentiert werden.
Mehrere Entities können auf dem selben Mesh-Objekt basieren, wenn man mehrere Ko-
pien eines Meshes in der Szene darstellen will. Auch hier wird wieder die Mesh-Ressource
nur einmal geladen auch wenn mehrere Entities mit dem Mesh-Objekt assoziiert sind.

Man erzeugt ein Entity durch den Aufruf der SceneManager::createEntity()-Methode.


Man gibt dem Entity einen eindeutigen Namen und speziziert das zugrunde liegen-
de Mesh-Objekt. Der Szenenmanager sorgt dafür, dass das Mesh durch Aufrufen des
MeshManager-Ressourcenmanager geladen wird.

Entities sind nicht Bestandteil der Szene solange sie nicht an einem Szenenknoten (Sce-
neNode, siehe Kapitel 3.1.6 auf Seite 29) angehängt werden. Durch das Anhängen an
Szenenknoten werden komplexe hierarchische Beziehungen zwischen den Positionen und
Orientierungen von Entities ermöglicht. Man verändert die Positionen von Knoten, um
indirekt die Positionen der Entities zu verändern.

Zu einem Mesh können auÿerdem mehrere Materialien (siehe Kapitel 3.1.7 auf Seite
29) deniert sein. So ist es möglich, verschiedenen Teilen des Meshes auch verschiedene
Materialien zuzuordnen. Jedes von einem Mesh erzeugte Entity benutzt automatisch die
vorgegebenen Materialien. Man kann dies aber für jedes Entity individuell ändern, um
mehrere Entities mit verschiedenen Materialien aber basierend auf demselben Mesh zu
erzeugen.

Wenn ein Entity basierend auf einem Mesh erzeugt wird, so ist es aus einem oder mehre-
ren SubEntity-Objekten zusammengesetzt, wobei jedes zu einem SubMesh-Objekt korre-

28
spondiert. Man greift auf das SubEntity über die Entity::getSubEntity()-Methode zu.
Jetzt kann man über den Aufruf von setMaterialName() das Material des SubEntity-
Objektes ändern. Dadurch kann man Entities basierend auf ein und demselben Mesh
individuell gestalten.

3.1.7 SceneNode-Objekt

Die Szene wird durch den Szenenmanager in einer baumbasierten Struktur verwal-
tet. Jeder Knoten entspricht einem SceneNode
-Objekt. Der Wurzelknoten wird über
SceneManager::getRootSceneNode() bereitgestellt. An jeden Knoten können weite-
re Knoten als Kindknoten über SceneNode::createChildSceneNode() angefügt wer-
den.

An jeden Knoten können Entities oder andere szenenrelevante Objekte wie Kameras oder
Lichtquellen angefügt werden. Szenenknoten haben eine Position, eine Skalierung und
eine Orientierung in der Szene. Ändert man z.B. die Position eines Knotens so ändert
sich auch die Position der Kindknoten relativ zum aktuellen Knoten. Dies beeinusst
natürlich auch die Position der angefügten Entities.

3.1.8 Materials

Das Material -Objekt kontrolliert, wie Objekte in der Szene gerendert werden. Es spezi-
ziert, welche Oberächen-Eigenschaften Objekte haben, wie z.B. diuse Farbe, Glanz-
farbe, Glanzstärke, wie viele Texturschichten vorhanden sind, welche Bilder als Texturen
dienen und wie sie miteinander kombiniert werden, welche Spezialeekte hinzukommen,
z.B. Umgebungsabbildung (Environment Mapping), wie die Texturen geltert werden
usw.

Materialien können entweder durch den Programmierer im Quellcode durch Aufrufen von
SceneManager::createMaterial() und Einstellen der verschiedenen Parameter erzeugt
werden oder durch das Spezizieren eines Skriptes welches erst zur Laufzeit geladen wird
(siehe Kapitel 4.1 auf Seite 44). Der groÿe Vorteil von Skripten liegt in der nicht mehr
notwendigen Neukompilierung des Quellcodes bei Änderung eines Skripts, da es ja erst
zur Laufzeit geladen wird. So erhält man ein schnelles Feedback auf Änderungen der
Materialskripts und auch Nicht-Programmierer können Einuss auf das Aussehen der
Szenenobjekte nehmen, da die Materialskripts leicht lesbar und verständlich sind.

Alles was die Erscheinung und das Rendern eines Objektes mit Ausnahme der Form
betrit wird in einem Material deniert.

29
Der Szenenmanager verwaltet die Hauptliste aller in einer Szene verfügbaren Materialien.
Zur Liste können Materialien durch den Aufruf von SceneManager::createMaterial
oder durch das Laden eines Meshes (was zur Folge hat, das auch die zugehörigen Ma-
terialien geladen werden) hinzugefügt werden. Die Eigenschaften von neuen Materialien
werden auf die von OGRE denierten Standardeigenschaften gesetzt oder durch das
jeweilige Materialskript festgelegt.

3.1.9 Overlays

Overlays sind 2- oder 3-dimensionale Elemente, die über die normale Szene gerendert
werden, um Benutzerinterfaces, Menüs oder Statistiken anzuzeigen. 2D-Elemente werden
für eben genannte Objekte und 3D-Elemente werden z.B. für Cockpits oder andere 3D-
Objekte genutzt, die über die normalen Szene gerendert werden.

Man kann Overlays entweder über SceneManager::createOverlay() erzeugen oder


man deniert sie in einem Overlayskript. Wobei letzteres wegen der schnellen Anpassung
und Veränderung ohne Neukompilierung praktikabler ist. Auch Overlayskripts werden
erst zur Laufzeit geladen. Overlays werden mit ihrer show()-Methode zur Anzeige ge-
bracht. Man kann auch mehrere Overlays gleichzeitig zeigen, wobei ihre Sichtbarkeits-
reihenfolge über die Methode Overlay::setZOrder() gesetzt wird.

Die OverlayElement-Klasse abstrahiert die Details von 2D-Elementen, die zu Overlays


hinzugefügt werden. Alle Elemente die zu Overlays hinzugefügt werden können, werden
von dieser Klasse abgeleitet. Die OverlayElement-Klasse verfügt über grundlegende Ei-
genschaften wie z.B. Gröÿe, Position, Materialname usw. Eigene Unterklassen können
die Funktionalität der OverlayElement-Klasse erweitern und spezialisieren.

Eine wichtige Unterklasse von OverlayElement ist OverlayContainer. Ein OverlayContai-


ner ist dasselbe wie ein OverlayElement, nur dass er wiederum andere OverlayElement-
Klassen enthält und sie zu logischen Gruppen verbindet.

Wenn man ein 2D Element als ein Overlay erzeugen will, ruft man OverlayManager::-
createOverlayElement() auf. Der Typ des zu erzeugenden Elements wird durch eine
zu übergebende Zeichenkette deniert. So erzeugt der Aufruf Overlay::Manager::get-
Singleton().createOverlayElement(Panel, new2DPanel) eine ache rechtecki-
ge Fläche mit dem eindeutigen Namen new2DPanel, die weitere Overlay-Elemente
beinhalten kann.

Um einen Container (wie z.B. das Panel) zu einem Overlay hinzuzufügen, ruft man
Overlay::add2D() auf. Wenn man Kind-Elemente zu einem Container hinzufügen will,
ruft man OverlayContainer::addChild() auf. Kind-Elemente können OverlayElements

30
oder OverlayContainer sein. Die Position eines Kind-Elements ist relativ zur linken obe-
ren Ecke seines Eltern-Elements.

[The06a] und [The06b]

3.2 Einrichten einer Entwicklungsumgebung und


Erstellen eines OGRE 3D-Projekts

In diesem Abschnitt wird die Einrichtung und die Kompilierung eines OGRE-Projekts
innerhalb der Visual Studio Entwicklungsumgebung von Microsoft erläutert. Die Pro-
jekteinrichtung erfolgt in anderen Entwicklungsumgebungen ähnlich.

Eine im Funktionsumfang reduzierte aber kostenlose Version von Microsoft Visual Studio
mit dem Namen Visual C++ Express steht kostenlos auf der Microsoft Homepage zum
Download1 . Diese Version ist für die Entwicklung mit OGRE bereits geeignet. Weil
Visual C++ Express auf die Erstellung von .net-Anwendungen ausgelegt ist benötigt
man zusätzlich das Win32-SDK2 , denn OGRE-Anwendungen laufen nicht innerhalb der
.net-Runtime sondern sind klassische Win32-Anwendungen.

Man installiert Visual C++ und das Win32-SDK. Das Win32-SDK muss in die Ent-
wicklungsumgebung integriert werden. Man wählt dazu im Visual C++ den Menüpunkt
Extras/Optionen aus und markiert im linken Fensterbereich Projekte und Projekt-
mappen/VC++ Verzeichnisse. Unter Verzeichnisse anzeigen für selektiert man den
Punkt Includedateien und fügt über das entsprechende Symbol das Verzeichnis  <Pfad
zum Win32-SDK>/Include  hinzu. Anschlieÿend trägt man bei Ausführbare Dateien
<Pfad zum Win32-SDK>/bin
das Verzeichnis   und bei Bibliotheksdateien das Ver-
<Pfad zum Win32-SDK>/lib
zeichnis   ein.
corewin_express.vsprops
Jetzt önet man die Datei   im Verzeichnis  <Pfad zu Vi-
sual C++>/VC/VCProjectDefaults  und tauscht die Zeile  AdditionalDependencies
=kernel32.lib gegen  AdditionalDependencies=kernel32.lib user32.lib
gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib
oleaut32.lib uuid.lib aus. Anschlieÿen löscht man in der Datei  AppSettings.htm 
die Kommentare in den Zeilen 441 bis 444.

Für die kommerzielle Version Visual Studio sind die obengenannten Schritte nicht nö-
tig.

1 http://www.microsoft.com/germany/msdn/vstudio/express/default.mspx
2 erhältlich unter: http://www.microsoft.com/downloads/details.aspx?FamilyId=
A55B6B43-E24F-4EA3-A93E-40C0EC4F68E5\&displaylang=en

31
Nun lädt man das OGRE-SDK herunter 3 und installiert es. Um die Installation zu prü-
fen, önet man in Visual C++ die Projektmappe Samples_vc8 aus dem Ordner  <Pfad
zum OGRE-SDK>/samples . Dieses Projekt enthält einige OGRE-Beispielprogramme.
Klickt man nun im Menü auf Erstellen/Projektmappe erstellen erzeugt Visual C++
alle Beispiele und legt sie unter  <Pfad zum OGRE-SDK>/bin/debug  ab.

Ein eigenes OGRE-Projekt erstellt man indem man über Projekt/Neu und Neue
Win32-Anwendung ein neues Projekt erzeugt. Man muss dem Projekt noch die OGRE-
Verzeichnisse bekannt geben. Dazu fügt man in den Projekteigenschaften unter Kon-
gurationseigenschaften bei C++/Allgemein dem Punkt Zusätzliche Include-Verzeich-
nisse den Eintrag  <Pfad zum OGRE-SDK>/include  hinzu. Genau so ergänzt man
unter Linker/Allgemein die Zusätzliche Bibliotheksverzeichnisse um  <Pfad zum
OGRE-SDK>/lib , sowie unter Linker/Eingabe die Zusätzliche Abhängigkeiten um
den Eintrag  OgreMain_d.lib. Abschlieÿend muss man noch sicherstellen, dass das er-
<Pfad zum OGRE-SDK>/bin/debug
stellte Programm die Dateien aus dem Verzeichnis  
beziehungsweise  <Pfad zum OGRE-SDK>/bin/release  ndet oder man kopiert die Da-
teien aus diesen Verzeichnissen in die entsprechenden Verzeichnisse der eben erstellten
Anwendung. [Sch06]

3.3 Eine Hello World-Anwendung

Folgende exemplarische HelloWorld-Anwendung initialisiert das OGRE-Rendersystem,


lädt ein Mesh mitsamt den zugehörigen Materialien und Texturen und deniert einen
Betrachter der Szene (Kamera) und eine Lichtquelle.

Man erstellt ein neues OGRE-Projekt gemäÿ den Schritten in Kapitel 3.1.9 auf Seite 31.
Zuerst bindet man die OGRE-Engine über einen #include-Befehl ein:
# include < Ogre .h >

Es werden auÿerdem weitere Header für den Fehlerdialog, für EventListener und KeyE-
vents benötigt. Eine ausführliche Erklärung dieser erfolgt später.
# include < OgreErrorDialog .h >
# include < OgreEventListener .h >
# include < OgreKeyEvent .h >

Das Benutzen des OGRE-Namensraumes, indem alle OGRE-Klassen enthalten sind,


erspart viel Tipparbeit, denn sonst müsste vor jede OGRE-Klasse das Präx Ogre::
vorangestellt werden.
using namespace Ogre ;
3 erhältlich unter http://www.ogre3d.org

32
Innerhalb der main()-Funktion wird der Programmcode in einen try/catch-Block aus-
geführt. Der Code im catch-Block erzeugt im Fehlerfall ein Fenster (createErrorDia-
log()), das die Fehlermeldung (ermittelt über getFullDescription()) anzeigt und vom
PlattformManager erzeugt wird, der alle betriebssystemabhängigen Elemente, wie z.B.
das eben erzeugte Dialogfenster, kapselt. Als allererstes erzeugt man im try-Block ein
Root-Objekt:
Root * mRoot = new Root ( " Plugins . cfg " , " Ogre . cfg " , " ogre . log " );

Dieses Objekt stellt den Kern des OGRE-Systems dar und ist Ausgangspunkt für alles
weitere (siehe Kapitel 3.1, Seite 24). Das erste Argument gibt den Pfad zu einer Kon-
gurationsdatei an, die OGRE mitteilt welche Plugins genutzt werden sollen. Plugins
sind z.B. das DirectX- und auch das OpenGL-Rendersystem und verschiedene Szenen-
manager. Eine beispielhafte  Plugin.cfg
-Datei sieht folgendermaÿen aus:
# Define plugin folder
PluginFolder =.

# Define plugins
Plugin = RenderSystem_Direct3D9
Plugin = RenderSystem_GL
Plugin = Plugin_ParticleFX
Plugin = Plugin_BSPSceneManager
Plugin = Plugin_OctreeSceneManager
Plugin = Plugin_CgProgramManager

Die ogre.log-Datei dient zum Protokollieren von OGRE-Ausgaben und Fehlermeldun-


gen. In der Datei  Ogre.cfg  werden die Einstellungen OGRE-Kongurationsdialoges
gespeichert. Diesen Dialog präsentiert man dem Benutzer mit:
mRoot - > showConfigDialog ();

Nun erstellt man ein Fenster, in das die Engine zeichnet:


RenderWindow * mWindow = mRoot - > initialise ( true , " HelloWorld " );

Um nun ein 3D-Modell mitsamt Materialdenitionen und Texturen in der OGRE-Anwen-


dung nutzen zu können, muss man den ResourceManager verwenden, der sich um
sämtliche externen Daten kümmert. Um dem Ressourcenmanager ein Verzeichnis mit
externen Daten bekanntzugeben, benutzt man folgenden Funktionsaufruf:
ResourceGroupManager :: getSingleton (). addResourceLocation
( " < Pfad zum OGRE - SDK >\\ media \\ materials \\ scripts " ,
" FileSystem " );

Der zweite Parameter FileSystem gibt an, dass es sich beim ersten Parameter um ein
Verzeichnis handelt. Alternativ kann man auch externe Daten wie 3D-Modelle, Materia-
lien und Texturen in ZIP-Archive packen. Dann würde der zweite Parameter Zip sein.

33
Man muss noch 3 weitere Pfade für dieses Beispiel angeben, siehe dazu den Quellcode auf
Seite 82. Nachdem dem Ressourcenmanager alle Verzeichnisse bekanntgegeben wurden,
initialisiert man den Datenbestand mit:
ResourceGroupManager :: getSingleton (). initialiseAllResourceGroups ();

Um nun 3D-Modelle im Raum zu platzieren benötigt man einen Szenenmanager, der


sich um alle szenenrelevanten Objekte kümmert:
SceneManager * mSceneManager = mRoot - > createSceneManager ( ST_GENERIC );

Jetzt erzeugt man ausgehend von einem dem OGRE-SDK beiliegenden Mesh ein kon-
kretes Objekt, das man in der Szene platzieren kann:
Entity * mOgreEntity = mSceneManager - > createEntity ( " OgreEntity1 " ,
" ogrehead . mesh " );

Mit dieser Zeile wurde ein Entity mit dem eindeutigen Namen OgreEntity1 aus der
Mesh-Datei ogrehead.mesh erzeugt. Man kann über den Namen jederzeit einen Ver-
weis auf das entsprechende OGRE-Objekt bekommen, z.B. über mSceneManager->get-
MovableObject(<Name>). Um die Darstellung des Objekts mit der passenden Textur
kümmert sich OGRE automatisch, wenn die .mesh-Datei den zugehörigen Materialna-
men enthält und der Ressourcenmanager diesen bereits in einer eingelesenen Material-
datei entdeckt hat und ihm auch alle benötigten Texturen zur Verfügung stehen. Bisher
ist das Entity aber noch nicht sichtbar. Es muss erst an einen Szenenknoten gehängt
werden. Dazu erzeugt man ein SceneNode-Objekt mit einem eindeutigen Namen und
einer Position in der Szene und verbindet anschlieÿend den Szenenknoten und das zuvor
erstellte Entity:
SceneNode * mOgreSceneNode = mSceneManager - > getRootSceneNode () - >
createChildSceneNode ( " Ogre1Node " , Vector3 (0 ,0 ,0));
mOgreSceneNode - > attachObject ( mOgreEntity );

Der neue Szenenknoten ist ein Kindknoten des bereits existierenden Wurzelknotens
(RootSceneNode). Neue Szenenknoten können entweder an den Wurzelknoten oder
an bestehende Szenenknoten anghängt werden um hierarchische Beziehungen der Kno-
ten zu erzeugen. Jetzt bendet sich das Objekt zwar in der Szene aber es ist aufgrund
einer fehlenden Beleuchtung noch unsichtbar. Das Hintergrundlicht (AmbientLight)
setzt man auf einen geringen Grauwert:
mSceneManager - > setAmbientLight ( ColourValue (0.1 ,0.1 ,0.1 ,1));

Und man erzeugt ein Licht mit der Position (-80, 80, 200) und dem Namen Light1:
Light * mLight = mSceneManager - > createLight ( " Light1 " );
mLight - > setPosition ( -80 , 80 , 200);

34
Es fehlt nur noch eine Kamera, die die Szene aufnimmt:
Camera * mCamera = mSceneManager - > createCamera ( " Camera1 " );

Man stellt sie an der Position (40, 40, 100) auf und lässt sie auf den Koordinatenursprung
schauen:
mCamera - > setPosition ( Vector3 (40 ,40 ,100));
mCamera - > lookAt (0 ,0 ,0);

Das vom Standpunkt der Kamera aus erzeugte Bild muss nun in einem Viewport zur
Anzeige gebracht werden:
Viewport * mViewport = mWindow - > addViewport ( mCamera );

Welcher normalerweise das gesamte Fenster ausfüllt:


mCamera - > setAspectRatio ( Real ( mViewport - > getActualWidth ()) /
Real ( mViewport - > getActualHeight ()));

Um die Anwendung auf Tastendruck beenden zu können, benötigt man noch die selbst-
geschriebene Klasse Keyboard:
Keyboard * mKeyboard = new Keyboard ();

Diese Klasse vereint die Interfaces sogenannter FrameListener (siehe auch Kapitel 3.4
auf Seite 37 und KeyListener, deren Methoden wie z.B. FrameListener::frameEnded()
werden nach jedem gezeichneten Bild oder KeyListener::keyClicked bei jeder ge-
drückten Taste aufgerufen. Die Klasse muss beim Root-Objekt registriert werden:
mRoot - > addFrameListener ( mKeyboard );

Auÿerdem muss ein EventProcessor initialisiert werden, der die Ereignisse von gedrück-
ten Tasten an die Keyboard-Klasse weiterleitet:
EventProcessor * mEventProcessor = new EventProcessor ();
mEventProcessor - > addKeyListener ( mKeyboard );
mEventProcessor - > initialise ( mWindow );
mEventProcessor - > startProcessingEvents

Zum Abschluss startet man die OGRE-Renderschleife mit:


mRoot - > startRendering ();

Im Anschluss erfolgt die Denition der Keyboard-Klasse. Sie wird von den OGRE-
Klassen FrameListener und KeyListener abgeleitet:

35
class Keyboard : public KeyListener , public FrameListener
{
// Öffentliche Methoden
public :
Keyboard ();
virtual void keyClicked ( KeyEvent * e );
virtual void keyPressed ( KeyEvent * e );
virtual void keyReleased ( KeyEvent * e );
virtual void frameEnded ( const FrameEvent & evt );

// private Variablen
private :
bool EscWasPressed ; // wurde ESC - Taste gedrückt
}

Der Konstruktor setzt die private Variable auf false:


Keyboard :: Keyboard () : mEscWasPressed ( false ) {}

Die KeyClicked()-Methode wird bei jedem Tastendruck aufgerufen:


void Keyboard :: keyClicked ( KeyEvent * e )
{
// Falls ESC - Taste gedrückt wurde
if (e - > getKey () == KC_ESCAPE
{
// Setze Variable auf true
mEscWasPressed = true ;

// Ereignis wurde verarbeitet


e - > consume ();
}
}

In den beiden anderen Keyboard-Methoden geschieht nichts. Sie müssen aber überschrie-
ben werden:
void Keyboard :: keyPressed ( KeyEvent * e ) {}
void Keyboard :: keyReleased ( KeyEvent * e ) {}

In der frameEnded()-Methode wird nach jedem Frame überprüft, ob die mEscWasPressed-


Variable gesetzt wurde. Wenn ja, dann liefert die Methode false zurück und das Pro-
gramm wird automatisch beendet.
bool Keyboard :: frameEnded ( const FrameEvent & evt )
{
return ! mEscWasPressed ;
}

36
Abbildung 3.2: Das HelloWorld-Renderfenster

Man sollte nun nach dem Starten des Programms und der Wahl der OGRE-Optionen
im Kongurationsdialog ein Renderfenster ähnlich der Abbildung 3.2 auf Seite 37 sehen.
Der vollständige Quellcode ist auf Seite 82 zu nden. [Sch06]

3.4 FrameListener

In der bisherigen HelloWorld-Anwendung konnte man bisher nur einmal zu Beginn


der Anwendung Einuss auf die Szene nehmen. Nachdem Root::startRendering()
aufgerufen wurde, bendet sich die Anwendung in der Renderschleife. FrameListener
implementieren ein Interface, das die Methoden frameStarted() und frameEnded()
zur Verfügung stellt. Die OGRE-Renderschleife sieht läuft folgendermaÿen ab:

• Das Root-Objekt ruft die frameStarted()-Methode aller registrierten FrameLis-


tener auf
• Das Root-Objekt rendert das aktuelle Bild
• Das Root-Objekt ruft die frameEnded()-Methode aller registrierten FrameListener
auf

Die Schleife läuft solange, bis entweder eine frameStarted()- oder eine frameEnded()-
Methode false zurückgibt (z.B. als Reaktion auf einen Tastendruck). Die beiden Me-
thoden sind in der Klasse FrameListener virtuell, d.h. man muss immer eigene Klas-
sen davon ableiten, die eine konkrete Implementierung für diese Methoden enthalten.
Konkrete FrameListener müssen über Root::addFrameListener() beim Root-Objekt
registriert werden. Folgender Quellcodeausschnitt zeigt einen konkreten FrameListener,
dem ein Verweis auf eine Animation als Konstruktorparameter übergeben wird und der

37
am Anfang jedes Bildes den Fortschritt der Animation um die seit dem letzten Aufruf
von frameStarted() vergangene Zeitspanne erhöht (siehe auch Kapitel 5.3 Animation
in OGRE 3D auf Seite 67):
class AnimListener : public FrameListener
{
protected :
public :
// Der Konstruktor mit Verweis auf eine Animation
AnimListener ( AnimationState * animState )
{
mAnimState = animState ;
}

bool frameStarted ( const FrameEvent & evt )


{
// Vergangene Zeit seit dem letzten Bild addieren
mAnimState - > addTime ( evt . timeSinceLastFrame );
}

protected :
// Der Verweis auf die Animation
AnimationState * mAnimState ;
};

Man könnte dasselbe Verhalten auch in frameEnded() implementieren. Auÿerdem ist es


dem Anwendungsentwickler überlassen, einen groÿen FrameListener zu schreiben oder
die Funktionalität auf mehrere kleinere FrameListener aufzuteilen. Zu beachten ist da-
bei nur, dass man nicht vorhersagen kann, in welcher Reihenfolge OGRE die einzelnen
FrameListener aufruft.

Im Programm muss nun eine konkrete FrameListener-Instanz erzeugt und beim Root-
Objekt registriert werden:
// Zeiger auf eine Animation , muss noch initialisiert werden
AnimationState * animState ;
...

// Aufrufen des Konstruktors mit der Animation als Parameter


AnimListener * animListener = new AnimListener ( animState );

// Registrieren des FrameListeners


mRoot - > addFrameListener ( animListener );

Die Klasse ExampleFrameListener bietet bereits eine beispielhafte Implementation ei-


nes FrameListeners und realisiert die Steuerung der Kamera über Maus- und Tastaturein-
<Pfad zum OGRE-SDK>/samples/include/Example-
gaben. Die Klasse ist in der Datei
FrameListener.h deniert.

38
3.5 Ein interaktives Beispiel: Kamera- und
Charaktersteuerung

Dieses Beispiel erweitert die HelloWorld-Anwendung um eine interaktive Kamerasteue-


rung und um eine interaktive Steuerung eines Charakters über die Tastatur. Die Tas-
tatureingaben werden dabei gepuert verarbeitet, d.h. sobald eine Taste gedrückt wird,
wird über einen KeyListener automatisch ein KeyListener::keyPressed-Ereignis aus-
gelöst. Sobald die Taste losgelassen wird, wird ein KeyListener::keyReleased-Ereignis
ausgelöst. Diese Methode hat gegenüber ungepuerter Eingabe den Vorteil, dass nicht
in jedem Bild der komplette momentane Status der Tastatur abgefragt werden muss,
sondern nur auf einzelne Ereignisse reagiert werden muss.

Die Verschiebung der Kamera erfolgt über die Pfeiltasten und die Drehung über die
Tasten W, A, S und D. Der Charakter wird über die Pfeiltasten auf dem Nummernblock
bewegt.

3.5.1 Kamerasteuerung

Zur Kamerasteuerung werden einige neue Variablen benötigt, die am Anfang der Quell-
codedatei als globale Variablen deklariert werden:
SceneNode * mCameraNode ; // Szenenknoten der Kamera
Vector3 mTranslateVector = Vector3 :: ZERO ; // Verschiebungsvektor
Radian mRotX = Radian (0); // Rotation um die X - Achse
Radian mRotY = Radian (0); // Rotation um die Y - Achse
const Real mMoveScale = 150; // Geschw . der Bewegung
const Degree mRotScale = Degree (60); // Geschw . der Rotation

Innerhalb der main()-Methode wird die Kamera an einen Szenenknoten angefügt und auf
die Startposition (40, 40, 100) gesetzt. Abschlieÿend wird die Kamerablickrichtung
auf den Koordinatenursprung gesetzt:
Camera * mCamera = mSceneManager - > createCamera ( " Camera1 " );
mCamera - > setNearClipDistance (5);

// Erstellen eines Szenenknotens für die Kamera


mCameraNode = mSceneManager - > getRootSceneNode () - >
createChildSceneNode ( " CameraNode " , Vector3 (0 , 0 , 0));

// Kamera anhängen
mCameraNode - > attachObject ( mCamera );

// Auf Startposition setzen


mCameraNode - > translate (40 , 40 , 100);

39
// Blickrichtung auf Koordinatenursprung
mCameraNode - > setDirection ( - mCameraNode - > getPosition ());

Die Tastaturabfrage wird in der Methode Keyboard::keyPressed() durchgeführt. Da-


bei wird immer geprüft, welche Taste gedrückt wurde und dementsprechend eine Kompo-
nente des Vektors mTranslateVector um den in mMoveScale denierten Betrag erhöht
oder verringert wird:
void Keyboard :: keyPressed ( KeyEvent * e )
{
// vor und zurück
if (e - > getKey () == KC_UP )
mTranslateVector . z -= mMoveScale ;
if (e - > getKey () == KC_DOWN )
mTranslateVector . z += mMoveScale ;

// links und rechts


if (e - > getKey () == KC_LEFT )
mTranslateVector . x -= mMoveScale ;
if (e - > getKey () == KC_RIGHT )
mTranslateVector . x += mMoveScale ;

// hoch und runter


if (e - > getKey () == KC_PGUP )
mTranslateVector . y += mMoveScale ;
if (e - > getKey () == KC_PGDOWN )
mTranslateVector . y -= mMoveScale ;

// Rotation um X - Achse
if (e - > getKey () == KC_A )
mRotX += mRotScale ;
if (e - > getKey () == KC_D )
mRotX -= mRotScale ;

// Rotation um Y - Achse
if (e - > getKey () == KC_W )
mRotY -= mRotScale ;
if (e - > getKey () == KC_S )
mRotY += mRotScale ;
}

Auÿerdem muss die Veränderung des Bewegungsvektors und der Rotationsvariablen


beim Loslassen der entsprechenden Taste rückgängig gemacht werden um die Bewegung
wieder zu stoppen. Dies geschieht in der Methode Keyboard::keyReleased() einfach
durch Vertauschen der Vorzeichen bei der Zusweisung der Werte:
void Keyboard :: keyReleased ( KeyEvent * e )
{
// vorzurück
if (e - > getKey () == KC_UP )

40
mTranslateVector . z += mMoveScale ;
if (e - > getKey () == KC_DOWN )
mTranslateVector . z -= mMoveScale ;

// Für die anderen Richtungen analog


...

// rotx
if (e - > getKey () == KC_W )
mRotY += mRotScale ;
if (e - > getKey () == KC_S )
mRotY -= mRotScale ;

...
}

Abschlieÿend wird der Kameraszenenknoten und damit auch die Kamera entsprechend
verschoben. Dies geschieht einmal pro Bild in der Keyboard::frameStarted()-Methode.
Zuerst wird die Rotation um die X- und Y-Achse zugewiesen:
bool Keyboard :: frameStarted ( const FrameEvent & evt )
{
mCameraNode - > yaw ( mRotX * evt . timeSinceLastFrame );
mCameraNode - > pitch ( mRotY * evt . timeSinceLastFrame );

Nun wird die Kamera entsprechend ihrer Blickrichtung um den aus mTranslateVector
bekannten Betrag verschoben, wobei die ganze Verschiebung durch die vergangene Zeit
seit dem letzten Bild (evt.timeSinceLastFrame) skaliert wird:
mCameraNode - > translate ( mCameraNode - > getOrientation () *
mTranslateVector * evt . timeSinceLastFrame );

return true ;
}

3.5.2 Charaktersteuerung

Zur Steuerung des Charakters werden ebenfalls einige globale Variablen benötigt:
SceneNode * mOgreSceneNode ; // Der Charakter - Szenenknoten
AnimationState * mAnimWalk ; // Die Lauf - Animation
AnimationState * mAnimIdle ; // Die Warte - Animation
Vector3 mRobotTranslate = Vector3 :: ZERO ; // Verschiebungsvektor
Radian mRobotRot = Radian (0); // Rotationsvariable

In der main()-Methode wird nun statt dem Ogre-Kopf der Roboter-Charakter aus der
Datei robot.meshgeladen:

41
Entity * mOgreEntity = mSceneManager - > createEntity ( " OgreEntity1 " ,
" robot . mesh " );

Es werden die beiden Animationen in die jeweiligen Variablen geladen:


mAnimWalk = mOgreEntity - > getAnimationState ( " Walk " );
mAnimIdle = mOgreEntity - > getAnimationState ( " Idle " );

Die beiden Animationen sollen als Schleife durchlaufen werden und die Warte-Animation
wird aktiviert:
mAnimWalk - > setLoop ( true );
mAnimIdle - > setLoop ( true );
mAnimIdle - > setEnabled ( true );

Die Steuerung des Roboters wird in der keyPressed()-Methode ähnlich der Kamera-
steuerung realisiert. Zusätzlich wird bei jedem Tastendruck jeweils die Warte-Animation
deaktiviert und die Lauf-Animation aktiviert:
if (e - > getKey ()== KC_NUMPAD8 )
{
// Verschiebungsvektor setzen
mRobotTranslate . x += mMoveScale / 4;

// Warte - Animation deaktivieren


mAnimIdle - > setEnabled ( false );

// Lauf - Animation aktivieren


mAnimWalk - > setEnabled ( true );
}
if (e - > getKey ()== KC_NUMPAD2 )
{
mRobotTranslate . x -= mMoveScale / 4;
mAnimIdle - > setEnabled ( false );
mAnimWalk - > setEnabled ( true );
}
if (e - > getKey ()== KC_NUMPAD4 )
{
mRobotRot += mRotScale ;
mAnimIdle - > setEnabled ( false );
mAnimWalk - > setEnabled ( true );
}
if (e - > getKey ()== KC_NUMPAD6 )
{
mRobotRot -= mRotScale ;
mAnimIdle - > setEnabled ( false );
mAnimWalk - > setEnabled ( true );
}

Beim Loslassen der Taste müssen die Änderungen am Verschiebungsvektor und an der
Rotationsvariablen zurückgesetzt werden und jeweils wieder die Lauf-Animation deak-

42
tiviert und die Warte-Animation aktiviert werden:
if (e - > getKey ()== KC_NUMPAD8 )
{
mRobotTranslate . x -= mMoveScale / 4;
mAnimIdle - > setEnabled ( true );
mAnimWalk - > setEnabled ( false );
}

...

if (e - > getKey ()== KC_NUMPAD6 )


{
mRobotRot += mRotScale ;
mAnimIdle - > setEnabled ( true );
mAnimWalk - > setEnabled ( false );
}

Jetzt muss man nur noch in der frameStarted()-Methode die Animationen um die
jeweils seit dem letzten Bild vergangene Zeit weiterlaufen lassen und den Roboter den
gemachten Eingaben entsprechend rotieren und bewegen:
// Animation updaten
mAnimIdle - > addTime ( evt . timeSinceLastFrame );
mAnimWalk - > addTime ( evt . timeSinceLastFrame );

// Roboter rotieren
mOgreSceneNode - > yaw ( mRobotRot * evt . timeSinceLastFrame );

// Roboter verschieben
mOgreSceneNode - > translate ( mOgreSceneNode - > getOrientation () *
mRobotTranslate * evt . timeSinceLastFrame );

Als Ergebnis erhält man eine interaktive Anwendung mit Kamera- und Charaktersteue-
rung wie in Abbildung 3.3 auf Seite 43 zu sehen ist. Der komplette Quellcode ist in
Anhang A.2 auf Seite 85 zu nden.

Abbildung 3.3: Das Renderfenster der interaktiven Anwendung

43
4 Das OGRE 3D-Skriptsystem

Im heutigen Anwendungsentwicklungsprozess teilt sich das Entwicklungsteam meist in


Programmierer und Artists (3D-Modellierer, Textur-Ersteller usw.) auf. Das OGRE-
Skriptsystem ermöglicht es, Anwendungsinhalte ohne Neukompilation zu verändern und
bietet eine einfache Schnittstelle, um auch Nichtprogrammierern Einuss auf die An-
wendung zu nehmen. Die Funktionalität, die durch Skripte bediet werden, können auch
durch Methodenaufrufe fest im Quelltext programmiert werden, aber dies sollte eher die
Ausnahme sein. Alle OGRE-Skripte werden bei der Programminitialisierung geladen.

4.1 Material-Skripte und Shader

Materialskripte sind die wohl mächtigsten OGRE-Skripte, bestimmen sie doch die kom-
plette Darstellung der zu rendernden Objekte. Die Skripte werden in einem C++ ähnli-
chen Pseudoformat geschrieben. Es können mehrere Materialien in einem Skript deniert
werden jedoch müssen alle Skripte einen global eindeutigen Namen haben. Materialien
können von anderen bereits denierten Materialien mit dem Doppelpunkt : kopiert
werden. Dabei überschreibt man im neuen Material nur die veränderten Eigenschaften
(ähnelt der Vererbung in C++). Jedes Material enthällt mindestens eine Technique,
welche wiederum mindestens einen Pass enthält.

4.1.1 Techniques

Eine Technique stellt eine Möglichkeit zur Verfügung ein Objekt zu rendern. Eine einfa-
che Materialdeniton enthält also nur eine Technique. Da die heutige Grakkarten sich
stark in ihrer Leistungsfähigkeit unterscheiden, kann man das nur tun, wenn man sicher
ist, dass sich das Material von allen Grakkarten auch darstellen lässt. Aufgrund des-
sen ist es ratsam, weitere Techniques mit einfacheren Anforderungen an die Hardware
in einem Material zu denieren. OGRE wählt dann automatisch die beste Technique,
die von der Hardware unterstützt wird. Auÿerdem kann man Techniques eine Distanz
zuweisen, sodass weiter entfernte Objekte mit einfacheren Techniques gerendert werden,
um eine bessere Performance zu erzielen.

44
Techniques können über die Eigenschaft scheme einem Renderschema zugeordnet wer-
den. Durch die Denition von z.B. den 3 Schemas Hoch, Mittel und Niedrig kann
man die Anwendung schnell an die vorhandene Hardware anpassen. Wobei bei der Ein-
stellung Hoch alle Objekte mit den bestmöglichen Techniques gerendert werden (, die
mit der Eigenschaft scheme Hoch deniert sind) und bei Niedrig auf weniger perfor-
mancehungrige Techniques zurückgegrien wird.
Die Eigenschaft lod_index weist eine Technique einer Entfernung zu, die im Materi-
al über die Eigenschaft lod_distance deniert wurde. Dies ermöglicht eine weniger
rechenintensive Technique für weiter enfternte Objekte.

4.1.2 Passes

Ein Pass ist ein Renderdurchlauf für ein Objekt mit festgelegten Rendereinstellungen.
Eine Technique kann bis zu 16 Passes beinhalten. Je mehr Passes, desto aufwendiger wird
die Berechnung. Passes enthalten Attribute, Textureinheiten und eventuell Verweise auf
Vertex- und Pixelshader (siehe Kapitel 4.1.3 auf Seite 47).

Hier folgt eine auszugsweise Beschreibung der möglichen Attribute in einem Pass. Für
ausführliche Informationen siehe [The06b].

Allgemeine Attribute

Das Attribut scene_blend legt fest, wie der Pass mit bereits gerenderten Inhalten der
Szene überblendet wird.
Über depth_check bestimmt man, ob der Tiefenbuer1 vor dem Rendern überprüft wird.
depth_write bestimmt, ob das aktuelle Objekt in den Tiefenbuer schreibt. Overlay-
Elemente, wie z.B. Menüs oder Buttons, sollten nicht in den Tiefenbuer schreiben.
cull_hardware bestimmt, ob die Hardware Flächen aufgrund der Anordnung ihrer Ver-
tizes vom Rendering ausschlieÿt. Sind die Vertizes von der Kamera aus gesehen im
Uhrzeigersinn angeordnet und ist das Atribut auf clockwise gesetzt, so werden sie vom
Rendering ausgeschlossen. Ist das Attribut auf none gesetzt, so werden die Flächen von
beiden Seiten gezeichnet, was z.B. bei durchsichtigen Objekten sinnvoll ist. Das Attri-
but polygon_mode bestimmt, wie das Objekt gerendert wird. solid rendert das Objekt
normal, wireframe rendert nur die Auÿenlinien der Flächen (als Drahtgitter) und point
rendert nur die Punkte des Polygons.
Das Attribut iteration bestimmt, ob der Pass mehrfach gerendert werden soll. Auÿer-
dem kann mit dem Setzen des Attributes auf once_per_light der Pass für jedes Licht
durchlaufen werden.
1 DerTiefenbuer (auch Z-Buer genannt) enthält die Tiefeninformation bereits gerenderter Objekte.
Liegt das aktuelle Objekt hinter einem bereits gezeichneten wird es nicht gerendert.

45
Attribute für die Fixed-Function Pipeline

Folgende Attribute haben nur Auswirkungen auf das Renderergebniss solange die Fixed-
Function Pipeline von OGRE benutzt wird. Bei Verwendung Shadern haben diese keine
Auswirkungen.

Die Attribute ambient2 , diffuse3 , specular4 und emissive5 beschreiben die entspre-
chenden Farben des Materials.
Über das Attribut lighting aktiviert und deaktiviert die dynamische Beleuchtung.
Über shading legt man auÿerdem fest, ob das Objekt flat-6 , gouraud-7 oder phong-
8
schattiert wird.

Textureinheiten

Das Attribut texture bestimmt über einen Dateinamen, welche Textur verwendet wird
und um welchen Texturtyp es sich dabei handelt. Unterstützte Typen sind 1d (eine Tex-
tur, die nur einen Pixel hoch ist, nützlich in Shadern, wenn man eine aufwendige Funk-
tion vor der Laufzeit in eine Textur kodiert, eine sogenannte Look-Up Tabelle), 2d (eine
normale Textur), 3d (eine Volumentextur, also mehrere 2D-Texturen, die über dreidi-
mensionale Texturkoordinaten in Shadern adressiert werden, z.B. nützlich für animierte
Texturen) und cubic(zusammengesetzte Textur aus 6 zweidimensialen Texturen, die auf
die Innenseiten eines Würfels projiziert werden, wird z.B. für Umgebungs-Mapping ver-
wendet).
tex_adress_mode steuert wie die Textur wiederholt wird. Mögliche Werte sind wrap
(Textur wird wiederholt), clamp (Ende der Textur wird breitgezogen), mirror (Textur
wird an den Rändern gespiegelt) und border (Textur wird an den Ränder auf eine mit
tex_border_colour denierte Farbe gesetzt).
Wie eine Textur aus bestimmten Betrachtungswinkeln und beim Übergang zweier MIP-
Stufen geltert wird, bestimmt das Attribut filtering. Mögliche Werte in qualitativ
aufsteigender Reihenfolge sind none(es wird keine Filterung vorgenommen und auch
kein MIP-Mapping angewendet), bilinear(Textur wird über einen 2x2-Filter interpo-
liert und es wird eine MIP-Stufe ausgewählt, aber nicht zwischen zwei Stufen überblen-
det), trilinear(wie bilinear, nur das hier zwei MIP-Stufen überblendet werden) und

2 Die ambiente Farbe bestimmt die Grundfarbe des Objekts ohne Beleuchtung.
3 Die diuse Farbe bestimmt die Farbe des Objekts, die durch einfallendes Licht reektiert wird.
4 Die spekulare Farbe bestimmt die Glanzfarbe des Objekts.
5 Die emissive Farbe bestimmt die Farbe mit der das Objekt unabhängig von Lichtquellen oder am-
bienten Farbeinstellungen gezeichnet wird.
6 Jede Objektäche wird mit einer Farbe gezeichnet. Diese wird nicht interpoliert.
7 Die Farbe jedes Vertex wird linear über die Fläche interpoliert.
8 Die Vertexnormalen werden über die Fläche interpoliert und somit die Farbe pro Pixel berechnet.
Dies ergibt ein realistischeres Ergebniss bei höherem Berechnungsaufwand.

46
anisotropic(wie trilinear, nur das hier der wirkliche Anstieg der Fläche zur Filterung
herangezogen wird und nicht nur ein einfacher 2x2-Filter verwendet wird).
Für weitere Attribute, insbesondere zum Animieren der Texturen, siehe [The06b].

4.1.3 Shader in OGRE 3D

Ein Shader ist ein kleines Programm, um die nalen Oberächeneigenschaften eines 3D-
Modells oder Bildes zu berechnen. Aufgrund ihrer Natur sind sie sehr gut für parallele
Berechnung geeignet. Mehrere parallele Prozessoren auf aktuellen Grakkarten berech-
nen somit mehrere Dreiecke und die resultierenden Pixel gleichzeitig. Shader sind in
speziellen Programmiersprachen geschrieben und ersetzen grundlegende Funktionen der
traditionellen Fixed-Function-Pipeline wie z.B. Transformationen, Beleuchtung und Tex-
turoperationen. Dabei gibt es auf der Grakkarte mehrere Vertex-Einheiten, die Vertex-
Shader (wird für jedes Vertex des zu rendernden Objekts einmal ausgeführt) und mehrere
Pixel-Einheiten, die Pixel-Shader (wird für jedes auf dem Bildschirm sichtbare Pixel ein-
mal ausgeführt.
OGRE bietet volle Unterstützung für Vertex- und Pixelshader bis zum aktuellen Sha-
dermodell 3.0. Somit sind alle modernen Beleuchtungs-(z.B. PerPixel-Lighting, Bump-,
Normal- und Parallax Mapping) und Materialsimulationen(z.B. Wasser- und Glasmate-
rialien) und Eekte (z.B. High Dynamic Range Rendering, Nebel und Tiefenunschärfe)
in OGRE möglich. Die Shader können in Assembler, DirectX9 HLSL, OpenGL GLSL
oder in Cg vorliegen.

Deklarieren von Shadern

Shader müssen erst in einem Skript deklariert werden, bevor sie von einem Materi-
al benutzt werden können. Dies kann entweder in dem entsprechenden Materialskript
direkt oder in einer .program-Datei geschehen. Die Deklaration umfasst einen eindeu-
tigen Namen, den Dateinamen des Shaderquellcodes und für welches Shadermodell der
Shader kompiliert werden soll. Folgendes Beispiel deklariert einen HLSL Vertexshader
mit dem Namen PerPixelLighting.Vertex_Shader, der aus der Datei  PerPixelLight-
ning.Vertex_Shader.source  geladen und für das Shadermodell 2.0 kompiliert wird:
vertex_program PerPixelLighting . Vertex_Shader hlsl
{
source PerPixelLightning . Vertex_Shader . source
target vs_2_0
entry_point vs_main

default_params
{
param_named_auto lightPosition light_position_object_space 0

47
param_named_auto eyePosition camera_position_object_space
param_named_auto worldViewProj worldviewproj_matrix
param_named shininess float 10
}
}

Der optionale default_params-Abschnitt deniert Defaultwerte für an den Shader zu


übergebende Parameter. Diese können dann später im Materialskript überschrieben wer-
den. Diese Vorgehensweise hält die Materialdenition schlanker, so werden sich z.B. ver-
schiedenen Variationen eines Materials, das den obengenannten Shader benutzt, nur in
dem Parameter shininess unterscheiden.

Shader in einem Material-Pass verwenden

Innerhalb eines Passes in einem Materialskript kann man Vertex- und Pixelshader, die
zuvor in einem Skript deklariert wurden, referenzieren:
vertex_program_ref PerPixelLighting . Vertex_Shader
{
param_named_auto worldViewProj worldviewproj_matrix
param_named shininess float 20
}

Hier wird der Vertexshader PerPixelLighting.Vertex_Shader aufgerufen und zwei Pa-


rameter übergeben, die eventuelle Defaultparameter überschreiben. Parameter können
über einen Namen (param_named) oder über einen Index (param_indexed übergeben
werden. Wobei der Index sich auf die Reihenfolge der Variablendeklaration innerhalb des
Shaders bezieht und in 4-Element-Blöcken angeordnet ist. Wenn man z.B. eine oat4-
Variable an Index 0 deklariert hat, so bendet sich die nächste oat4-Variable an Index 1,
wenn man eine 4x4-Matrix an Index 0 deklariert hat, so bendet sich die nächste Variable
an Index 4 usw. Die Übergabe mit Namen ist also einfacher zu benutzen. Es gibt eine Rei-
he von Variablen, die OGRE zu Verfügung stellt und automatisch aktualisiert. Dazu zäh-
len z.B. verschiedenen Transformationsmatrizen (z.B. world_matrix, view_matrix,
projection_matrix usw.), Lichtquelleneigenschaften (z.B. light_position, light_-
direction, light_diffuse_colour usw.), Kameraeigenschaften (z.B. camera_po-
sition) und Variablen, die sich über die Zeit verändern, um Animationen zu reali-
sieren.
Die Übergabe der Lichtquellenposition an den Shader in die Variable mit Namen light-
Pos geschieht folgendermaÿen:
param_named_auto lightPos light_position 0

Dasselbe nur als Übergabe mit Index:


param_indexed_auto 0 light_position 0

48
Die abschlieÿende 0 gibt an, dass die Position der dem Objekt nächsten Lichtquelle
übergeben wird. Höhere Werte beziehen sich dann auf weiter entfernte Lichtquellen.

Shader und Schatten

Wenn man Schatten benutzt, kann es zu einigen Schwierigkeiten in Kombination mit


Vertexshadern kommen, da diese die Geometrie des Objekts verändern können, das
OGRE-Schattensystem aber keinen Zugri auf diese Veränderungen hat.

Wenn man Stencil-Schatten benutzt, so sollte man nur skeletale Animation einsetzen,
denn jede andere Geometriedeformation hat keine Auswirkung auf den berechneten
Schatten. Man kann dann nur akzeptieren, dass der Schatten nicht korrekt zur geren-
derten Geometrie passt oder auf Schattenberechnung ganz verzichten (es sei denn, man
realisiert die Schattenberechnung ebenfalls in einem Shader, was aber ein sehr komplexes
Gebiet ist).

Wenn man Textur-Schatten benutzt, sind Geometriedeformationen kein Problem.

4.1.4 Ein einfacher Beispielshader

Im folgenden Beispiel wird ein einfacher Shader erarbeitet, der ein 3D-Modell durch
diuses Licht einer Punktlichtquelle beleuchtet und das Ergebnis mit einer Textur ver-
sieht. Der Shader wird in HLSL für das Shadermodell 2.0 geschrieben. Da die Licht-
berechnung im Vertexshader geschieht spricht man auch von PerVertex-Lighting. Das
genauere PerPixel-Lighting würde es erfordern, dass die Lichtberechnungen im Pixels-
hader ausgeführt werden, was aber zu einer schlechteren Performance führt.

Der Vertex-Shader

Zuerst deniert man, welche Informationen des zu rendernden Objektes dem Shader als
Eingabe dienen. Für den Beispielshader benötigen wir die Position, die Normale und die
Texturkoordinate des aktuellen Vertex;
struct VS_INPUT
{
float4 Pos : POSITION ;
float3 Normal : NORMAL ;
float2 Txr : TEXCOORD0 ;
};

49
Die Ausgabe des Vertexshaders ist zugleich Eingabe des Pixelshaders und umfasst die
(transformierte) Position, die Texturkoordinaten und die berechnete Lichtintensität:
struct VS_OUTPUT
{
float4 Pos : POSITION ;
float2 Txr : TEXCOORD0 ;
float4 Color : COLOR0 ;
};

Auÿerdem müssen folgende Variablen deklariert werden, deren Werte von der Anwen-
dung (in diesem Falle das OGRE-System) bereitgestellt werden müssen:
float4x4 matViewProjection ; // Transformationsmatrix
float4 viewPosition ; // Die Position des Betrachters
float4 LightAmbient ; // Das ambiente Licht
float4 LightPos ; // Position der Lichtquelle
float4 LightColor ; // Farbe der Lichtquelle

Für die Lichtberechnung wird der Übersichtlichkeit wegen eine Funktion deniert, die
aus der Vertex-Position und -Normalen und der Lichtposition und -farbe die Intensität
des reektierten Lichtes auf dem 3D-Modell berechnet:
float4 Light_PointDiffuse ( float3 VertPos , float3 VertNorm ,
float3 LightPos , float4 LightColor )
{
// Berechne die Richtung aus der das Licht auf den Vertex trifft und
// normalisiere diesen Vektor
float3 LightDir = normalize ( LightPos - VertPos );

// Berechne die Lichtintensität bezüglich des Winkels zwischen


// der Lichtrichtung und der Vertex - Normalen über das Punktprodukt
float AngleAttn = dot ( VertNorm , LightDir );

// Da dieser Wert negativ werden kann , muss er auf den Wertebereich


// [0 , 1] beschnitten werden
AngleAttn = clamp (0 , 1 , AngleAttn );

// Berechne finale Beleuchtungsintensität


return LightColor * AngleAttn ;
}

Nun folgt der eigentliche Vertexshader-Code:


VS_OUTPUT vs_main ( VS_INPUT In )
{
VS_OUTPUT Out ;

// Berechne die in den Bildschirmraum projizierte Position des Vertex


Out . Pos = mul ( matViewProjection , In . Pos );

50
// Leite Texturkoordinaten unverändert weiter
Out . Txr = In . Txr ;

// Das ambiente Licht definiert den Grundanteil der Lichtintensität


float4 Color = LightAmbient ;

// Berechne Lichtintensität durch die zuvor definierte Funktion


Color += Light_PointDiffuse ( In . Pos , In . Normal ,
LightPos , LightColor );

// Gib berechnete Farbe an den Pixelshader weiter


Out . Color = Color ;

return Out ;
}

Der Pixel-Shader

Der Pixelshader benötigt als Eingabe nur die Texturkoordinaten und die berechnete
Lichtintensität. Da ein Vertex eher selten genau einem Pixel auf dem Bildschirm ent-
spricht, werden die berechneten Werten zwischen verschiedenen Vertizes interpoliert.
struct PS_INPUT
{
float2 Txr : TEXCOORD0 ;
float4 Color : COLOR0 ;
};

Als Variable wird nur noch eine Textur benötigt:


sampler Texture0 ;

Der Pixelshader hat folgenden einfachen Aufbau:


float4 ps_main ( PS_INPUT In )
{
// Die endgültige Farbe des aktuellen Pixels ergibt sich aus
// der interpolierten Lichtintensität , die im Vertexshader
// berechnet wurde multipliziert mit dem Farbwert der Textur
float4 finalColor = In . Color * tex2D ( Texture0 , In . Txr );

// Die berechnete Farbe wird ausgegeben


return finalColor ;
}

Die Abbildung 4.1 auf Seite 52 zeigt den Shader in RenderMonkey von ATI (siehe auch
Kapitel 7.7 auf Seite 79). Der komplette zusammenhängende Quellcode ist in Listing

51
Abbildung 4.1: Der PerVertex-Lighting Beispielshader

A.5 auf Seite 92 zu nden. Für weitere Informationen zur Shaderprogrammierung siehe
[SL04], [Ros04], [Fer04] und [Eng05].

4.2 Compositor-Skripte

Compositor-Skripte bieten eine einfache Möglichkeit PostProcessing-Eekte wie z.B.


Bewegungs- und Tiefenunschärfe oder High Dynamic Range Rendering zu realisieren.
Auch hier können die Skripte verändert werden, ohne dass die Anwendung neu kompi-
liert werden muss. Im Quelltext muss nur ein Compositor einem Viewport zugewiesen
und aktiviert werden:
CompositorManager :: getSingleton (). addCompositor ( viewport ,
compositorName );
CompositorManager :: getSingleton (). setCompositorEnabled ( viewport ,
compositorName , enabledOrDisabled );

Um einen PostProcessing-Eekt zu erzeugen, wird erst die gesamte Szene in eine Textur
gerendert. Diese wird über einen Pixelshader auf einen Viereck gerendert, das den gan-
zen Bildschirm umfasst. Das Ergebniss dieses Vorganges kann entweder das endgültige
auf dem Bildschirm erscheinende Bild oder aber wieder eine Zwischentextur sein, die
durch weitere Compositor-Passes bearbeitet wird.
Mehrere Compositors können in einem sogenannten CompositorChain hintereinander
angeordnet werden, d.h. der nächste Compositor erhält als Eingabe die Ausgabe des vor-
herigen Compositors. Diese Kette wird automatisch von OGRE angelegt und die Reihen-
folge richtet sich nach der Reihenfolge der CompositorManager::getSingleton().add-
Compositor()-Aufrufe. Die Hauptkomponenten von einem Compositor sind Techniques,
Target Passes und Passes. Das Skript ist wieder in einem Pseudo-C++-Dialekt geschrie-
ben und jeder Compositor muss einen global eindeutigen Namen zugewiesen bekom-
men.

52
4.2.1 Techniques

Eine Compositor-Technique ist ähnlich wie eine Material-Technique, eine Beschreibung,


um einen gewünschten Eekt zu erzielen. Ein Compositor-Skript kann mehr als eine
Technique beinhalten, um eine Ausweich-Technique für ältere Hardware bereitzustellen.
Techniques können die Elemente texture, target und target_output beinhalten. Wo-
bei texture eine Textur deniert, die in einem target_pass verwendet wird. Sie wird
über einen Namen, Höhe und Breite und ein Pixel-Format deniert:
texture RenderTarget1 512 512 PF_A8R8G8B8
texture RenderTarget2 target_width target_height PF_FLOAT32_RGB

Die erstere Zeile deniert ein RenderTarget, das 512 mal 512 Pixel groÿ ist und bei dem
jeder Pixel aus jeweils 8 Bit für Alpha-, Rot-, Grün- und Blauwert zusammengesetzt ist.
Die zweite Zeile gibt ein RenderTarget an, das so groÿ wie das Renderfenster ist und bei
dem die Pixel in einem speziellen Flieÿkommaformat gespeichert sind, welches z.B. für
HDR-Rendering benötigt wird.

4.2.2 Target Pass

Ein Target Pass ist ein Renderdurchlauf, der entweder in eine Render-Textur oder die
nale Ausgabe rendert. Das Element target <Name> rendert in eine Textur, während
das Element target_output <Name> die nale Ausgabe erzeugt. Die Inhalte beider
Elemente sind gleich. Der Compositor kann jedoch nur ein target_output-Element
aber mehrere target-Elemente haben. Folgende Attribute können in diesen Elementen
vorkommen:
Über input wird denniert, ob die Textur vor dem Rendering mit Inhalt gefüllt wird.
Der Wert previous sorgt dafür, dass die Textur mit der Original-Renderszene, falls es
der erste Compositor in der Kette ist, oder mit der Ausgabe des vorherigen Compositors
gefüllt wird. Der Wert none belässt die Textur unbefüllt, so dass diese vollständig durch
den aktuellen Pass gefüllt werden muss.
Das Attribut only_initial on sorgt dafür, dass dieser Pass nur einmal bei Aktivieren
des Compositors gerendert wird. Dies ist nützlich, wenn dieser Pass nur statische Inhalte
enthält.
Das Attribut material_scheme teilt diesen Pass ähnlich dem gleichlautenden Attribut
in Materialskripten einer Qualitätseinstellung zu.

53
4.2.3 Compositor Pass

Ein Compositor Pass ist ein in einem Target Pass durchgeführtes Rendering. Es gibt
folgende Pass-Typen:

• clear: Dieser Pass setzt den Inhalt eines Buers einer Zieltextur auf einen be-
stimmten festen Wert. Dies könnte z.B. den Farbbuer auf eine bestimmte Farbe
oder den Tiefenbuer auf einen bestimmten Wert setzen.
• stencil: Dieser Pass konguriert den Stencilbuer und die zugehörigen Stencil-
funktionen für nachfolgende Passes.
• render_scene: Dieser Pass rendert die komplette Szene in eine Textur.
• render_quad: Dies rendert ein bildschirmumfassendes Viereck durch ein deniertes
Materialskript in eine Textur. Das eigentliche PostProcessing wird durch diesen
Pass gerendert.

Das Attribut material bestimmt das Material für einen render_quad-Pass. Man be-
nutzt Pixelshader in diesem Material um PostProcessing-Eekte zu rendern und übergibt
Texturen an das Material über das Attribut input. Für weitere Informationen zu den
Buer- und Stenciloperationen siehe [The06b].

4.2.4 Ein einfacher Beispiel-Kompositor

Folgendes Beispiel zeigt den Aufbau eines PostProcessing-Eekts, das einen Weichzeich-
ner zweimal über die gerenderte Szene laufen lässt (siehe Abbildung 4.2 auf Seite 55):
compositor SimpleBlur
{
technique
{
texture rt target_width target_height PF_R8G8B8
texture rt2 target_width target_height PF_R8G8B8

target rt { input previous }

target rt2
{
input none

pass render_quad
{
material ScreenSpace . SimpleBlur
input 0 rt
}
}

54
target_output
{
input none

pass render_quad
{
material ScreenSpace . SimpleBlur
input 0 rt2
}
}

}
}

(a) Ausgangsbild (b) Endergbniss nach 2 Durchläufen des Weich-


zeichner-Kompositors

Abbildung 4.2: Ein einfacher Weichzeichner

Für das zugehörige Materialskript und den Pixelshader siehe Listing A.3 auf Seite 90.

4.3 Particle-Skripte

Particle-Skripte denieren Partikelsystem-Vorlagen, die im Quellcode mehrfach instan-


tiiert werden können. Jedes Partikelsystem setzt sich aus allgemeinen Eigenschaften
und ein oder mehreren Partikelemittern und optionalen Partikelaektoren zusammen.

55
Nachfolgend werden einige ausgewählte Eigenschaften erläutert (für eine vollständige
Eigenschaftenliste siehe [The06b]):

• quota - Setzt die maximale Anzahl an Partikeln. Wenn dieses Limit erreicht wird,
so werden für eine Weile keinen neuen Partikel emittiert bis einige zerstört wurden
(weil sie z.B. ihre Lebensdauer überschritten haben).
• material - Legt das Material für alle Partikel des Systems fest. Jedoch kann jedes
Partikel sein Aussehen über sein Farbattribut ändern.
• particle_width und particle_height - Bestimmt die Breite und Höhe eines
Partikels in Weltkoordinaten.
• cull_each - Ist standardmäÿig auf false gesetzt, so dass das Partikelsystem über
seinen umgebenden Hüllquader9 auf Sichtbarkeit geprüft wird. Dies ist vorteilhaft
bei kleinen lokalen Partikelsystemen. Bei Systemen, die jedoch einen groÿen Be-
reich abdecken, kann es zu einer besseren Performance führen, wenn jedes Partikel
auf Sichtbarkeit geprüft wird.
• local_space - Normalerweise bewegen sich Partikel nachdem sie emittiert wur-
den unabhängig vom Emitter im Weltkoordinatensystem. Sollen sich die Partikel
jedoch in Abhängigkeit zum Emitter bewegen (im lokalen Raum des Emitters) so
wird diese Eigenschaft auf true gesetzt.
• billboard_type - Partikel werden über sogenannte Billboards gerendert. Das sind
Rechtecke, die aus 2 Dreiecken bestehen, die rotiert werden, um in eine bestimmmte
Richtung zu zeigen. Normalerweise sind diese Rechtecke direkt der Kamera zuge-
wandt (Parameter point). Dies ist für kugelförmige Partikel, wie z.B. Lichteekte,
gut geeignet. Bei andere Eekte, wie z.B. Laserfeuer, erzielt man bessere Ergebnis-
se mit Partikeln, die in eine eigene Richtung zeigen (Parameter oriented_self).
Der Parameter oriented_common richtet die Partikel an einem gegebenen Vektor
(Eigenschaft common_direction) aus.

4.3.1 Partikelemitter

Partikelemitter erzeugen Partikel in einem durch den Partikelemittertyp bestimmten


Raum und in zufälliger Verteilung. OGRE unterstützt die Typen Point, Box, Cylinder,
Ellipsoid, HollowEllipsoid und Ring. Nachfolgend werden einige ausgewählte Eigen-
schaften von Emittern erläutert (für eine vollständige Eigenschaftenliste siehe [The06b]):

• angle - Bestimmt den maximalen Winkel in Grad, indem die Partikel abweichend
zur Emitter-Richtung erzeugt werden.
• colour - Bestimmt eine Farbe, in der alle Partikel gerendert werden.

9 engl.: bounding box

56
• colour_range_start und colour_range_end - Legt einen Farbbereich fest, in-
nerhalb dessen Partikel zufällig gefärbt werden.
• direction - Legt die Richtung des Emitters relativ zu seinem Szenenknoten fest.
• emission_rate - Deniert, wie viele Partikel pro Sekunde emittiert werden.
• position - Bestimmt die Position des Emitters relativ zu seinem Szenenknoten.
• velocity - Legt eine feste Geschwindigkeit für alle emittierten Partikel fest.
• time_to_live - Deniert eine Anzahl von Sekunden, die der Partikel lebt bevor
er zerstört wird.
• duration - Deniert eine Anzahl von Sekunden, die der Emitter aktiv ist. Standard
ist hier 0 für unbegrenzte Aktivität.
• repeat_delay - Falls eine duration ungleich 0 angegeben wurde, deniert dieser
Wert die Zeitdauer in Sekunden bis der Emitter wieder aktiv wird.

Die Eigenschaften velocity, time_to_live, duration und repeat_delay können al-


ternativ über _min- und _max-Zusätze in einem bestimmten Bereich variiert werden.

4.3.2 Partikelaektoren

Aektoren nehmen auf verschiedene Weise Einuss auf ein Partikelsystem. Es gibt fol-
gende Aektoren in OGRE:

• LinearForce - Dieser Aektor addiert eine lineare Kraft, wie z.B. Wind und Gra-
vitation, zu allen Partikeln. Er wird deniert durch einen force_vector und ob
die Kraft addiert oder der Durchschnitt zwischen Kraft und dem Bewegungsvektor
gebildet wird (force_application average|add).
• Scaler - Skaliert Partikel um einen bestimmten Wert (rate).
• Rotator - Rotiert die Partikel, indem die Partikeltextur rotiert wird. Wird durch
Anfangs- und Endrotationsgrade (rotation_range_start und rotation_range_-
end) und durch Rotationsgeschwindigkeiten (rotation_speed_range_start und
rotation_speed_range_end) deniert.
• ColourFader - Dieser Aektor verändert die Farbe eines Partikels um einen be-
stimmten Wert pro Sekunde.

Für weitere Aekoren siehe [The06b].

57
4.3.3 Ein einfaches Beispiel-Partikelskript

Folgendes Particle-Skript deniert das in Abbildung 4.3 auf Seite 59 gezeigte Partikel-
system:
PurpleFountain
{
// Grundeinstellungen
material Examples / Flare2
particle_width 20
particle_height 20
cull_each false
quota 10000
billboard_type oriented_self

// Der Partikelemitter
emitter Point
{
angle 15
emission_rate 75
time_to_live 3
direction 0 1 0
velocity_min 250
velocity_max 300
colour_range_start 1 0 0
colour_range_end 0 0 1
}

// Gravitation
affector LinearForce
{
force_vector 0 -100 0
force_application add
}

// Ein Farb - Fader


affector ColourFader
{
red -0.25
green -0.25
blue -0.25
}
}

Für die Erstellung von Particle-Skripten empehlt sich der ParticleEditor (siehe Kapitel
7.4 auf Seite 76), der sofortiges optisches Feedback liefert und intuitiv zu bedienen ist.

58
4.4 Overlay-Skripte

Overlays werden nach allen anderen Objekten gerendert und verdecken damit darun-
terliegende Bereiche. Sie eignen sich für Menüs und Buttons oder sonstigen relevanten
Anzeigen wie z.B. einer Punkteanzeige oder eines Raumschi-Cockpits in Spielen. Man
kann Overlays direkt im Quellcode denieren oder in einem .overlay-Skript, welches die
Overlay-Dention in einer Pseudo-C++-Sprache enthält.

Overlay-Skripte enthalten die Denition von einem oder mehreren Overlays, die global
eindeutige Namen haben müssen. Jedes Overlay besteht aus der Eigenschaft zorder
und ein oder mehreren Elementen. zorder bestimmt die Reihenfolge, in der die Overlays
gerendert werden. Ein Overlay mit höherem Wert überdeckt ein Overlay mit niedrigerem
Wert.

Abbildung 4.3: Ein einfaches Partikelsystem

59
4.4.1 Elemente zu Overlays hinzufügen

Ein Overlay kann Elemente (ein 2D-Element, das keine weiteren Kindelemente hat)
und Container (enthält selber wieder Elemente und Container) beinhalten. Ein Element
oder Container setzt sich aus einem Typen und einem eindeutigen Namen zusammen. In
OGRE vordenierte Typen sind Panel, BorderPanel, TextArea und TextBox. Elemente
oder Container können folgende Eigenschaften enthalten:

• metrics_mode - Bestimmt, wie nachfolgende Positions- und Gröÿenangaben inter-


pretiert werden: pixel für absolute Angaben oder relative für relative Angaben.
• horz_align - Ordnet das Element in der Horizontalen an. Mögliche Werte sind
left, center und right.
• vert_align - Ordnet das Element in der Vertikalen an. Mögliche Werte sind top,
center und bottom.
• left - Setzt den horizontalen Ursprung des Elements relativ zu einem eventuellen
Eltern-Element.
• top - Setzt den vertikalen Ursprung des Elements relativ zu einem eventuellen
Eltern-Element.
• width - Setzt die Breite des Elements proportional zur Bildschirmgröÿe. 0.25 ent-
spricht z.B. einem Viertel der Bildschirmbreite.
• height - Setzt die Höhe des Elements proportional zur Bildschirmgröÿe.
• material - Bestimmt das Material, mit welchem das Element gerendert wird.
• caption - Setzt die Überschrift des Elements.
• rotation - Bestimmt, ob das Element gedreht werden soll. Erst folgt die Winkel-
angabe, danach die Angabe der Rotationsachse als Vektor. So dreht rotation 30
0 0 1 das Element 30 Grad um die x-Achse.

4.4.2 Standard-Overlay-Elemente

• Panel - Eine rechteckiger Bereich mit einem Hintergrundmaterial, der weitere Ele-
mente enthalten kann
• BorderPanel - Wie Panel, nur das ein separates Material für einen Begrenzungs-
rahmen gesetzt werden kann
• TextArea - Wird genutzt, um Text zu rendern
• TextBox - Wie TextArea, nur das man Text eingeben kann

Folgender Code zeigt eine beispielhafte Overlay-Dention:

60
// Der Name des Overlays
Overlays / BeispielOverlay
{
zorder 200

// erzeugen eines Panels


container Panel ( OverlayElements / TestPanel )
{
// in der Mitte zentrieren und nach oben verschieben
left 0.25
top 0
width 0.5
height 0.1

// Material , mit der das Panel gerendert wird


material Materials / TestPanelMaterial

// Ein weiteres Panel innerhalb des aktuellen Panels


container Panel ( OverlayElements / WeiteresPanel )
{
left 0
top 0
width 0.1
height 0.1
material Materials / WeiteresPanelMaterial
}
}
}

4.4.3 Font Denition-Skripte

OGRE benutzt texturbasierte Schriftarten, um z.B. den Text in einem Overlay-Element


zu rendern. Schriftarten werden in .fontdef-Dateien deniert. Man kann eine Schrift-
art aus einem TrueType-Font erzeugen lassen oder eine Schriftart-Textur bereitstellen,
die alle vorkommenden Zeichen in einer Textur unterbringt und für jedes Zeichen eine
Texturkoordinate gespeichert wird.

Eine existierende Schriftart-Textur verwenden

Diese Font-Dention ist vom Typ type image und die Eigenschaft source <Dateiname>
legt die Schriftart-Textur fest. Danach folgt eine Reihe von Zeichendenitionen in der
folgenden Form: glyph <Zeichen> <u1> <v1> <u2> <v2>, wobei die vier Koordinaten
das Rechteck beschreiben, in dem sich das Zeichen in der angegebenen Textur bendet.

61
Solche Koordinaten von Hand zu erzeugen ist sehr zeitaufwendig. Ein gutes Werkzeug für
die Generierung der Texturen als auch der Koordinaten ist der BitmapFontBuilder10 .

Eine Schriftart-Textur generieren

Diese Art der Font-Denition ist vom Typ type truetype und hier legt die Eigen-
schaft source <Dateiname> die zugrundeliegende TrueType-Schriftart fest. Die Eigen-
schaft size legt die Gröÿe der Zeichen in der generierten Textur fest und die Eigen-
schaft resolution bestimmt die endgültige Gröÿe der Zeichen, wenn sie gerendert wer-
den. Folgendes Skript erzeugt eine Schriftart mit dem Namen Schriftart1 aus der Datei
Schrift1.ttf
, die nun von Overlay-Elementen zur Textausgabe genutzt werden kann:
Schriftart1
{
type truetype
source Schrift1 . ttf
size 16
resolution 96
}

10 erhältlich unter: http://www.lmnopc.com/bitmapfontbuilder/

62
5 Schatten in OGRE 3D

5.1 Allgemeines

Schatten stellen eines der anspruchvollsten Gebiete der 3D-Programmierung dar. Es gibt
sehr verschiedene Ansätze zur Schattenberechnung, jedoch ist keiner perfekt und sie ha-
ben alle ihre spezischen Vor- und Nachteile. Trotzdem sind Schatten für die Glaub-
würdigkeit der dargestellten Szene wichtig, weil sie räumliche Beziehungen zwischen den
Objekten verdeutlichen. OGRE stellt mehrere Schattenimplementationen bereit.

5.1.1 Schatten in OGRE 3D aktivieren

Schatten sind standardmäÿig deaktiviert. Man aktiviert sie mit dem Aufruf der Scene-
Manager::setShadowTechnique()-Methode und der gewünschten Schattenart als Pa-
rameter:
mSceneMgr - > setShadowTechnique ( SHADOWTYPE_STENCIL_ADDITIVE );

Weitere Schattenarten sind SHADOWTYPE_STENCIL_MODULATIVE, SHADOWTYPE_TEXTURE_-


MODULATIVE und SHADOWTYPE_TEXTURE_ADDITIVE. Die Schattenart muss vor dem Laden
der 3D-Modelle gesetzt werden, da Modelle nun anders geladen werden. Auÿerdem be-
nötigt man mindestens eine Lichtquelle. Nicht jede Lichtquellenart wird von jeder Schat-
tenart unterstützt. Falls eine Lichtquelle keinen Schatten werfen soll, muss diese über
Light::setCastShadows(false) von der Schattenberechnung ausgeschlossen werden.
Auch Objekte müssen, falls gewünscht, von der Berechnung über SceneNode::setCast-
Shadows(false) ausgeschlossen werden. Aus Performance-Gründen kann die Entfer-
nung, bis zu der Schatten berechnet werden, über SceneManager::setShadowFar-
Distance() gesetzt werden. Auÿerdem können bestimmte Materialien über den Ma-
terialparameter receive_shadows false vom Empfang von Schatten ausgeschlossen
werden. Dies ist z.B. nützlich bei selbstleuchtenden oder transparenten Materialien.

63
5.1.2 Modulative Schatten

Modulative Schatten dunkeln im Schatten liegende Bereiche einer zuvor mit voller Lichtin-
tensität gerenderten Szene mit einer fest eingestellten Farbe ab. Erst wird die Szene mit
allen Schatten erhaltenden und werfenden Objekten gerendert. Danach dunkelt ein mo-
dulativer Rendering-Durchlauf pro Lichtquelle die im Schatten liegenden Bereiche ab.
Zum Schluss werden alle Objekte gerendert, die keine Schatten erhalten sollen. Dieses
Schattenmodell ist nicht sehr wirklichkeitsnah, da es bei mehreren Lichtquellen zu unna-
türlich dunklen Schatten kommen kann. Jedoch sind modulative Schatten mit statischen
in Texturen vorberechneten Schatten kompatibel.

5.1.3 Additive Light Masking

Beim Additive Light Masking wird die Szene in mehreren Durchläufen gerendert. Dabei
stellt jeder Durchlauf die Lichtberechnung einer Lichtquelle dar. Im Schatten liegende
Bereiche werden dabei ausmaskiert. Jeder Durchlauf addiert seine Berechnungsergebnis-
se zum vorigen Durchlauf. Diese Technik führt zu sehr realistischen Ergebnissen aber zu
Lasten der Performance aufgrund mehrerer Render-Durchläufe.

5.2 Stencil-Schatten

Stencil-Schatten werden durch Schatten-Volumen-Berechnung erzeugt. Das Schattenvo-


lumen wird durch folgenden Algorithmus berechnet:

• Finde alle Kanten, die zur Silhouette des Schatten werfenden Objektes gehören
• Expandiere alle gefundenen Kanten in entgegengesetzter Richtung zur Lichtquelle
• Schlieÿe das erzeugte Volumen mit einer Front- und einer Rückäche

Die Szene wird nun in drei Schritten gerendert (modulativer Schatten):

• Rendere die Szene als wenn sie komplett beleuchtet wäre


• Konstruiere mithilfe der Tiefeninformation der Szene eine Maske innerhalb des
Stencil-Buers, die Löcher in den Bereichen hat, die nicht innerhalb des Schatten-
Volumens liegen
• Rendere die Szene einmal pro Licht mithilfe der Stencil-Buer-Maske, um die im
Schatten liegenden Bereiche abzudunkeln

64
5.2.1 Das HelloWorld-Beispiel mit Stencil-Schatten

Nun wird das HelloWorld-Beispiel aus Kapitel 3.2 auf Seite 32 um Stencil-Schattenbe-
rechnung erweitert. Dabei soll der OGRE-Kopf einen Schatten auf eine darunterliegende
Ebene werfen. Um Stencil-Schatten in OGRE zu benutzen, muss zunächst die Schatten-
art über folgenden Funktionsaufruf gesetzt werde:
mSceneManager - > setShadowTechnique ( SHADOWTYPE_STENCIL_MODULATIVE );

Dieser Methodenaufruf muss vor dem Laden jeglicher 3D-Modelle geschehen. Da die
Schattenfarbe standardmäÿig auf Schwarz gesetzt ist, kann man den Schattenwurf etwas
abschwächen:
mSceneManager - > setShadowColour ( ColourValue (0.5 , 0.5 , 0.5));

Nun wird die Ebene deniert, auf die der Schatten fällt:
// Ein Ebenen - Mesh generisch über den MeshManager erstellen
MeshManager :: getSingleton (). createPlane ( " plane . mesh " , " Custom " ,
Plane ( Vector3 :: UNIT_Z , Vector3 :: ZERO ) , 250 , 250 , 10 , 10);

// Ein Entity aus dem Ebenen - Mesh erstellen


Entity * mGround = mSceneManager - > createEntity ( " GroundEntity " ,
" plane . mesh " );

// Material für die Ebene setzen


mGround - > setMaterialName ( " Examples / Rocky " );

// Neuen Szenenknoten erstellen und Ebene anhängen


SceneNode * mGroundNode = mSceneManager - > getRootSceneNode ()
-> createChildSceneNode ();
mGroundNode - > attachObject ( mGround );

Abbildung 5.1: Das HelloWorld-Beispiel mit Stencil-Schatten

65
// Ebene ausrichten und etwas nach unten verschieben
mGroundNode - > setOrientation ( Quaternion ( Radian ( - Math :: HALF_PI ) ,
Vector3 :: UNIT_X ));
mGroundNode - > translate (0 , -50 , 0);

Hier wird ein Ebenen-Mesh (plane.mesh) generisch über den MeshManager. erstellt.
Danach kann man wie gewohnt ein Entity mit dem Mesh laden. Das Ergebniss ist in
Abbildung 5.1 auf Seite 65 zu sehen.

5.3 Textur-basierte Schatten

Textur-basierte Schatten werden berechnet, indem getestet wird, ob ein Objekt von der
Lichtquelle aus gesehen sichtbar ist. Dieser Test geschieht durch einen Vergleich der
Tiefe des Objekts (von der Lichtquelle aus gesehen) mit einer Tiefentextur. Die Tiefen-
textur wird erstellt, indem die Szene von der Lichtquelle aus gerendert wird, dabei aber
nicht der Farbwert, sondern die Tiefeninformation gespeichert wird. Das Ergebniss ist
die sogenannte Schatten-Map. Diese Technik ist ungenauer als die Stencil-Technik, aber
in vielen Fällen performanter. Auÿerdem können die Schatten-Maps weiter bearbeitet
werden, um z.B. weiche Schatten zu erzeugen (dabei werden mehrere Schatten-Maps
in unterschiedlichen Auösungen berechnet und daraus ein nale Schatten-Map durch
Überblendung berechnet), was mit Stencil-Schatten nicht möglich ist. Ein wichtiger Fak-
tor für die Genauigkeit der Schatten ist die Auösung der Schatten-Map (Abbildung 5.2
auf Seite 66 zeigt die Auswirkung der Auösung der Schatten-Map auf das Renderer-
gebniss in der Beispielanwendung aus dem Kapitel 5.2). Je kleiner die Schatten-Map
desto ungenauer und weicher werden die Schatten. In OGRE wird diese Schattenart
folgenderweise aktiviert:
mSceneManager - > setShadowTechnique ( SHADOWTYPE_TEXTURE_MODULATIVE );

(a) Schatten-Map mit einer Auösung (b) Schatten-Map mit einer Auösung
von 1024 x 1024 Pixeln von 128 x 128 Pixeln

Abbildung 5.2: Textur-basierte Schatten in OGRE

66
6 Animation in OGRE 3D

6.1 Skeletale Animation

Unter skeletaler Animation versteht man die Deformation von 3D-Modellen mithilfe ei-
ner skeletalen Struktur (auch Bones, dt. Knochen, genannt). Dabei werden die Knochen
des Modells animiert, was zur Folge hat, dass auch die entsprechend zugewiesenen Verti-
zes bewegt werden. Jeder Vertex wird einem oder mehreren Knochen in einer bestimmten
Wichtung zugewiesen. Die skeletale Struktur wird bei der Erstellung des 3D-Modells in
einem 3D-Modelling-Programm, wie z.b. 3ds Max oder Maya, festgelegt und über einen
Exporter in das OGRE-.mesh- und -.skeleton-Format exportiert (siehe Kapitel 7.5 auf
Seite 76). Die Animationen werden in der .skeleton-Datei gespeichert, die automatisch
mit geladen wird, wenn eine .mesh-Datei geladen wird, zu der ebenfalls ein Skelett de-
niert und exportiert wurde. Folgende Leistungsmerkmale werden von OGRE dabei
unterstützt:

• Jedem Mesh kann genau ein Skelett zugewiesen werden


• Pro Skelett kann es beliebig viele Knochen geben
• Hierarchische vorwärtsgerichtete Kinematik der Knochen
• Mehrere benannte Animationen pro Skelett, z.B. Laufen, Rennen usw.
• Unbegrenzte Anzahl an Schlüsselbildern (Keyframes) pro Animation
• Lineare oder spline-basierte Interpolation zwischen Schlüsselbildern
• Ein Vertex kann mehreren Knochen mit einer bestimmten Wichtung zugewiesen
werden
• Einem 3D-Modell können gleichzeitig mehrere Animationen zugewiesen und mit
bestimmter Wichtung ineinandergeblendet werden

Skeletale Animation kann entweder vom Hauptprozessor oder in Vertexshadern von der
Grakkarte berechnet werden. Wobei letzteres aufgrund der Leistungsfähigkeit heutiger
Grakkarten empfehlenswert ist.

Folgender Quellcodeausschnitt lädt das mit OGRE-ausgelieferte Robotermodell


(robot.mesh) und die zugehörige Skelettdatei (robot.skeleton) und startet die Ani-

67
mation Walk:
// Variablen
AnimationState * mAnimationState ; // Zur Ansteuerung der aktuellen
// Animation
Entity * mEntity ; // Das Roboter - Entity
SceneNode * mNode ; // Der Roboter - Szenenknoten

// Laden des Roboters und Zuweisen zu einer Entity - Variablen


mEntity = mSceneMgr - > createEntity ( " Robot " , " robot . mesh " );

// Erzeugen eines Szenenknotens und Anhängen des Roboters


mNode = mSceneMgr - > getRootSceneNode () - > createChildSceneNode (
" RobotNode " , Vector3 ( 0.0 f , 0.0 f , 25.0 f ) );
mNode - > attachObject ( mEntity );

// mAnimationState verweist nun auf die " Walk " - Animation des Roboters
mAnimationState = mEntity - > getAnimationState ( " Walk " );

// Die Animation soll sich nach dem Abspielen wiederholen


mAnimationState - > setLoop ( true );

// Die Animation wird gestartet


mAnimationState - > setEnabled ( true );

Um eine Animation jedoch wirklich ablaufen zu lassen, muss noch die seit dem letzten
Bild vergangene Zeit addiert werden. Dies geschieht am besten in der frameStarted-
Methode einer OGRE::FrameListener-Klasse (siehe Kapitel 3.4 auf Seite 37):
mAnimationState - > addTime ( evt . timeSinceLastFrame );

Übergibt man der Methode negative Werte, so sind auch rückwärts ablaufende Anima-
tionen möglich.

Will man nun eine andere Animation startet, so beendet man die aktuelle, verweist auf
die neue Animation und startet diese:
mAnimationState - > setEnabled ( false );
mAnimationState = mEntity - > getAnimationState ( " Die " );
mAnimationState - > setLoop ( true );
mAnimationState - > setEnabled ( true );

Um eine weitere Animation gleichzeitig ablaufen zu lassen, holt man sich einen weite-
ren Verweis auf die zweite Animation und aktiviert diese. Über AnimationState::set-
Weight() kann die Gewichtung der Animation zu anderen gleichzeitig aktiven Anima-
tionen deniert werden:
AnimationState * mAnimationState2 ;
mAnimationState2 = mEntity - > getAnimationState ( " Idle " );

68
// Verringert die Gewichtung der zweiten Animation
mAnimationState2 - > setWeight (0.5);
mAnimationState2 - > setLoop ( true );
mAnimationState2 - > setEnabled ( true );

Für die Anwendung von skeletaler Animation im Kontext einer kompletten OGRE-
Anwendung siehe das interaktive Beispiel in Kapitel 3.5 auf Seite 39 sowie den zugehö-
rigen Quelltext im Anhang A.2 auf Seite 85.

6.2 Vertex-Animation

Bei der Vertex-Animation werden Informationen zur direkten Bewegung der zu einem
3D-Modell gehörenden Vertizes gespeichert. Diese Informationen sind direkt in der.mesh-
Datei enthalten. Vertex-Animation unterscheidet man nochmal in Morph- und Pose-
Animation, wobei Morph-Animation eine vereinfachte Form der Pose-Animation ist.

6.2.1 Morph-Animation

Bei der Morph-Animation werden zu jedem Schlüsselbild die absoluten Positionen al-
ler Vertizes eines 3D-Modells gespeichert. Man interpoliert nun linear zwischen einem
Schlüsselbild und dem ursprünglichen Erscheinungsbild des 3D-Modells und erzeugt so-
mit die Animationen. Es ist nicht möglich mehrere Schlüsselbilder zu überblenden. Diese
Art der Animation wird bei Objekten genutzt, die nicht durch skeletale Animation ani-
miert werden können, weil sich z.B. die Geometrie zu stark verformen soll.

6.2.2 Pose-Animation

Pose-Animation kombiniert mehrere Schlüsselbilder eines 3D-Modells mit einstellbaren


Gewichten zu einem Endresultat und wird häug für Gesichtsanimation von Charak-
teren, insbesondere für Lippensynchronisation verwendet. Die Vertex-Daten der einzel-
nen Posen werden als relative Osets zum ursprünglichen 3D-Modell gespeichert. Jede
Pose stellt bei der Gesichtsanimation einen Gesichtsausdruck dar. Diese können nun in-
einandergeblendet oder komplett zusammengemischt werden, sofern sie unterschiedliche
Bereiche des Gesichts beeinussen.

Sowohl Morph- als auch Pose-Animationen können entweder vom Hauptprozessor oder
von einem Vertexshader berechnet werden. Man sollte jedoch bei der Pose-Animation

69
beachten, dass mit der Anzahl der eingesetzten Posen auch die Performance deutlich
sinken kann.

Pose-Animation wird in OGRE ähnlich verwendet wie skeletale Animation. Man benötigt
eine AnimationState-Variable, die auf eine Pose-Animation verweist:
// Die Animationsvariable
AnimationState * mAnimState ;

// Der zu animierende Kopf


Entity * mOgreEntity = mSceneManager - > createEntity ( " OgreEntity1 " ,
" facial . mesh " );

// Modell an ein Szenenknoten binden


SceneNode * mOgreSceneNode = mSceneManager - > getRootSceneNode () - >
createChildSceneNode ( " Ogre1Node " , Vector3 (0 ,0 ,0));
mOgreSceneNode - > attachObject ( mOgreEntity );

// Die Animation starten


mAnimState = mOgreEntity - > getAnimationState ( " Speak " );
mAnimState - > setEnabled ( true );

Nun muss der Animation noch die vergangene Zeit seit dem letzten Bild in der frameStarted()-
Methode eines FrameListener (siehe Kapitel 3.4 auf Seite 37) hinzugefügt werden:
mAnimState - > addTime ( evt . timeSinceLastFrame );

Die Abbildung 6.1 auf Seite 70 zeigt den sprechenden Charakter, den dieses Beispiel
erzeugt.

Abbildung 6.1: Gesichtsanimation mit Pose-Animation

70
6.2.3 Kombination von skeletaler und Vertex-Animation

Skeletale und Vertex-Animation können gleichzeitig auf einem 3D-Modell benutzt wer-
den. Zuerst wird das Modell über die Vertex-Animation deformiert und erst danach wird
noch die skeletale Animation durchgeführt. Die Animation erfolgt immer auf Submesh-
Basis, d.h. man kann z.B. einen Charakter als einen Körper- und einen Kopf-Submesh
denieren und die skeletale Animation auf den Kopf- und Körper und die Pose-Animation
nur auf den Kopf anwenden. [The06b]

6.3 Szenenknoten-Animation

Szenenknoten-Animationen werden vom Szenenmanager erzeugt, um die Bewegung von


Szenenknoten und damit auch die der angehängten Objekte zu animieren. Im Grunde
funktioniert die Szenenknoten-Animation genauso wie die skeletale Animation. Nach-
dem man mit SceneManager::createAnimation() eine Animation erzeugt hat, de-
niert man noch einen NodeAnimationTrack pro Knoten, den man animieren will. Pro
NodeAnimationTrack legt man noch Schlüsselbilder fest, die Position, Orientierung und
Skalierung speichern, und zwischen denen man nun linear oder splinebasiert interpolieren
kann. [The06b]

Folgender Quellcodeabschnitt erzeugt eine Kamerafahrt:


// Erstellen einer splinebasierten Animation
// des Kamera - Szenenknotens
Animation * camAnim ;

// Animation mit Namen " CameraTrack " und einer


// Dauer von 10
camAnim = mSceneMgr - > createAnimation ( " CameraTrack " , 10);
camAnim - > setInterpolationMode ( Animation :: IM_SPLINE );

// Erzeugen eines Tracks für die Kamerafahrt


NodeAnimationTrack * track = camAnim - > createNodeTrack (0 , mCameraNode );

// Erstellen von Schlüsselbildern


// 1. Schlüsselbild an Position 0
TransformKeyFrame * key = track - > createNodeKeyFrame (0);
key - > setTranslate ( Vector3 (0 , 0 , 0));
// 2. Schlüsselbild an Position 2.5
key = track - > createNodeKeyFrame (2.5);
key - > setTranslate ( Vector3 ( -20 ,20 , -20));
key = track - > createNodeKeyFrame (5);
key - > setTranslate ( Vector3 (150 ,100 ,300));
key = track - > createNodeKeyFrame (7.5);
key - > setTranslate ( Vector3 (0 ,50 ,0));

71
// Letztes Schlüsselbild an letzter Position (10)
key = track - > createNodeKeyFrame (10);
key - > setTranslate ( Vector3 (0 , 80 , -30));

// Die komplette Animation wird in einem


// AnimationState - Objekt gespeichert
AnimationState * mCamAnimState ;
mCamAnimState = mSceneMgr - > createAnimationState ( " CameraTrack " );

// Animation starten
mCamAnimState - > setEnabled ( true );

6.4 Animation von numerischen Werten

Neben den bisher besprochenen Animationsarten kann man auÿerdem jeden numeri-
schen Wert animieren, dessen Klasse das Interface AnimableObject implementiert. Im-
plementiert eine Klasse dieses Interface, so stehen verschiedenen Methoden bereit, um
numerische Daten der Klasse mit dem generischen Animationssystem von OGRE zu
verbinden.

72
7 OGRE 3D-Tools und Exporter

7.1 XML-Konverter

Der OGRE XML-Konverter konvertiert binäre .mesh- und .skeleton-Dateien in XML1


und zurück. Die binären Dateien werden zur nalen Nutzung innerhalb der OGRE-
Anwendung verwendet. Die XML-Dateien werden zum einfachen Austausch von Daten
benutzt, da die meisten Mesh-Exporter XML-Dateien ausgeben und diese auch noch
von Hand editierbar sind. Auÿerdem kann der XML-Konverter zusätzliche Daten für
das Mesh erzeugen wie z.B. das Erzeugen von Begrenzungsboxen und von mehreren
Detailstufen (LODs) des Meshes. Das vorliegen von mehreren Detailstufen hat den Vor-
teil, dass OGRE automatisch für weiter vom Betrachter entfernte Objekte niedriger
aufgelöste Versionen des zugrunde liegenden Meshes nimmt, somit die Anzahl der zu
berechnenden Dreiecke minimiert wird und die Performance erhöht wird. Das Redu-
zieren der Geometriekomplexität erledigt der XML-Konverter recht gut, jedoch werden
nach diesem Vorgang die Texturen nicht mehr korrekt auf das Modell projiziert (siehe
Abbildung 7.1 auf Seite 74).

Die DTD2 der XML-kodierten .mesh- und .skeleton-Dateien ndet man im OGRE-
Quellcodeverzeichnis3 unter /Tools/XMLConverter/docs
.

Der XML-Konverter ist nicht im OGRE-SDK enthalten sondern muss separat herunter-
geladen werden4 .

So wird der XML-Konverter in der Eingabeauorderung benutzt:

OgreXMLConverter [Optionen] Quelldatei [Zieldatei]

Gibt man als Option -i an, so wird man durch die möglichen Optionen geleitet.
Optionen sind z.B. die Generierung von Detailstufen und ihre zugehörigen Entfernungen,
die Generierung von Tangentenvektoren für Normal-Mapping usw. Weitere Optionen

1 XML - Extensible Markup Language


2 Document Type Denition, Dokumenttypdeniton
3 OGRE-Quellcode erhältlich unter: http://www.ogre3d.org
4 OgreCommandLineTools erhältlich unter: http://www.ogre3d.org

73
(a) Original-Mesh (b) Details reduziert um 20 Prozent

(c) Details reduziert um 40 Prozent (d) Details reduziert um 80 Prozent

Abbildung 7.1: Auswirkung der Detailreduktion des XML-Konverters auf die Texturpro-
jektion

benden sich in der entsprechenden Readme-Datei des XML-Konverters.

7.2 MeshUpgrader

Der OGRE MeshUpgrader ist ebenfalls Bestandteil der OgreCommandLineTools und


dient dazu, binäre .mesh-Dateien älterer OGRE-Versionen auf die aktuelle Version zu
portieren. Man kann ältere .mesh-Dateien benutzen, jedoch könnte dies einen negativen

74
Einuss auf die Performance haben und man wird in der OGRE-Log-Datei auf die
Benutzung von älteren .mesh-Dateien hingewiesen und sollte dementsprechend auf die
neue Version upgraden.

So wird der MeshUpgrader in der Eingabeauorderung benutzt:

OgreMeshUpgrader [Optionen] Quelldatei [Zieldatei]

Auch hier wird man wieder mit der Option -i durch alle möglichen Optionen geleitet,
die denen des XML-Konverters entsprechen.

7.3 MeshViewer

Der OGRE MeshViewer steht auf der


OGRE-Homepage zum Download be-
reit und erlaubt es, sowohl binäre
.mesh-Dateien als auch ihre XML-
Pendants einzulesen und in einem
Grakfenster anzuzeigen (siehe Ab-
bildung 7.2 auf Seite 75).
Der MeshViewer bietet die Möglich-
keit das Modell von allen Seiten zu be-
trachten und sich die zugehörige Ma-
terialdention inklusive verwendeter
Texturen anzeigen und verändern zu
lassen. Sind in der .mesh-Datei Ani-
mationen gespeichert, so lassen sich
Abbildung 7.2: Der OGRE MeshViewer
diese einzeln abspielen. Es lassen sich
Information zur Mesh- und Submesh-
Geometrie wie Anzahl der Indizes und Vertizes und ob für die einzelnen Vertizes Nor-
malen und diuse oder spekulare Farben deniert sind. Der MeshViewer erlaubt die
Erstellung und Betrachtung von Detailstufen (LODs) des Meshes. Hier kommt es aber
zu den selben Problemen hinsichtlich der Texturen wie beim XML-Konverter, da der
MeshViewer diesen zur Konvertierung der Modelle benutzt. Ein weiterer Nachteil ist
die Bedingung, dass sich die einer .mesh-Datei zugehörigen Materialdenitionen und so-
mit auch die zugehörigen Texturen und Shader-Programme im selben Verzeichnis wie
der MeshViewer benden müssen. Das widerspricht der OGRE-typischen Aufteilung des
media-Ordners.
Für eine schnelle Überprüfung der aus einer Modelling-Anwendung exportierten Objek-
te eignet sich der MeshViewer jedoch sehr gut und man kann auch auf die komplette
Funktionalität des XML-Konverters zurückgreifen.

75
7.4 ParticleEditor

Der OGRE ParticleEditor steht eben-


falls auf der OGRE-Homepage zum
Download und ist ein Echtzeit-Editor
für OGRE-Partikelskripte mit sofor-
tigem graschen Feedback bei je-
der Änderung der Partikelsysteme.
Es werden alle Leistungsmerkma-
le der OGRE-Partikelskripte unter-
stützt. Man kann bereits bestehen-
de Skripte modizieren oder kom-
plett neue Partikelsysteme erschaen
und sich abschlieÿend die .particle-
Dateien zur Integration in eigene An-
wendungen ausgeben lassen. Der Edi-
tor ist intuitiv bedienbar. Im linken
Abbildung 7.3: Der OGRE ParticleEditor
Fenster lädt und speichert man Par-
tikelskripte. Im rechten Fenster de-
niert man die Grundparameter wie Partikelanzahl und -gröÿe sowie das benutzte Ma-
terial (siehe Abbildung 7.3 auf Seite 76). Desweiteren deniert man hier einen oder
mehrere Emitter mitsamt ihren vielfältigen Einstellungsmöglichkeiten und Aektoren
wie z.B. Kraftfelder und Farbmodikationen. Jede Änderung wird sofort im Echtzeit-
Renderfenster umgesetzt und vereinfacht das Entwickeln von komplexen Partikelsyste-
men enorm. Die Partikelskripte, Material- und Texturdateien müssen jedoch in bestimm-
ten Unterverzeichnissen des ParticleEditor-Verzeichnisses liegen, was leider ein ständiges
Hin- und Herkopieren zwischen diesem und dem Projektverzeichnis nötig macht, an dem
man gerade arbeitet. Obwohl der ParticleViewer nur die Versionsnummer 0.08 trägt,
macht er dennoch einen ausgewachsenen Eindruck und läuft auch recht stabil.
Ein anderer Partikeleditor mit ähnlichem Funktionsumfang mit dem Namen Particle Ac-
celerator kann unter http://www.tetracorp.net/dev/ParticleAccelerator1.0.zip
heruntergeladen werden.

7.5 Mesh-Exporter

Mesh-Exporter für OGRE sind die bevorzugte Möglichkeit OGRE-Anwendungen mit


Inhalten zu füllen. Modelling-Anwendungen bieten unerschöpiche Möglichkeiten zur
Erstellung von 3D-Modellen. Es sind Exporter für AC3D, Lightwave, Maya, Milkshape5 ,

5 kostenlos erhältlich unter: http://www.swissquake.ch/chumbalum-soft/

76
SoftImage XSI, Blender6 und 3ds Max verfügbar, wobei im Folgenden auf die beiden
letzteren näher eingegangen wird.

7.5.1 3ds Max-Exporter

Der 3ds Max-Exporter für OGRE erlaubt das Exportieren beliebiger 3D-Modelle, egal
mit welcher Modellierungsart sie erstellt wurden. Es können also auch Modelle, die als
NURBS7 oder Subdivision Surfaces erstellt wurden, exportiert werden. Diese werden
vor dem Exportieren implizit in editierbare Netze, also Listen von Dreiecken, konver-
tiert. Es werden auch die Texturkoordinaten und eine Materialdenition exportiert. Der
Exporter erstellt eine XML-Datei des Modells im OGRE-spezischen Format. Optional
kann man diese gleich durch den OGRE XML-Konverter in eine binäre .mesh-Datei um-
wandeln lassen, inklusive dessen Einstellungsmöglichkeiten bezüglich der Erzeugung von
LODs, Tangentenvektoren usw. (siehe Kapitel 7.1 auf Seite 73). Auÿerdem lassen sich
auch Modelle inklusive ihrer Animationen exportieren. Unterstützte Animationsarten
und -einstellungen werden in Kapitel 6 auf Seite 67 erläutert.

7.5.2 Blender-Exporter

Der Blender-Exporter für OGRE ermöglicht das Exportieren von Blender-3D-Modellen.


Es werden dabei alle Modellierungsarten auÿer Subdivision Surfaces unterstützt. Für ein-
zelne 3D-Modelle können die Vertexfarben, mehrere Materialien, Texturkoordinaten, die
Vertexnormalen und eventuelle Keyframe-Animationen, die auf ein ebenfalls exportier-
bares Skelett zurückgreifen exportiert werden. Beim Exportieren kann das Modell noch
skaliert und rotiert werden und entweder im Welt- oder im Objektkoordinatensystem
ausgegeben werden. Die Ausgabe erfolgt ähnlich dem 3ds Max Exporter als OGRE-
spezischen XML-Mesh-Format, welches anschlieÿend vom OGRE-XML-Konverter in
das binäre .mesh-Format gewandelt wird.

7.6 oFusion

oFusion8 ist eine Erweiterung zu Autodesks 3ds Max, die es ermöglicht, die erstellte Szene
inklusive aller erstellten Modelle, deren Materialien und Ausleuchtung innerhalb von
3ds Max in einem echten OGRE-Renderfenster zu betrachten. Man erhält ein sofortiges

6 kostenlos erhältlich unter: http://www.blender.org/


7 NURBS - Non Uniform Rational B-Spline
8 erhältlich unter http://www.ofusiontechnologies.com

77
optisches Feedback auf alle Änderungen der Szene und somit entfällt das häuge Testen
der veränderten Szene in der eigentlichen OGRE-Anwendung. oFusion erlaubt auch dem
Nichtprogrammierer hochwertige Inhalte für OGRE-Anwendungen in seiner bevorzugten
Software zu erstellen ohne sich um OGRE-spezische Details kümmern zu müssen.

Wichtigster Bestandteil ist das oFusion-Ansichtsfenster, welches sich nahtlos in 3ds Max
integriert (siehe Abbildung 7.4 auf Seite 78). Es werden 3D-Modelle, Lichter, Kameras,
Animationen, Materialien und Shader in der Echtzeit-Ansicht unterstützt.

oFusion unterstützt die 3ds Max-


Materialien Standard-, Blend-, Multi-
/Sub-Object-, Shell-Material und Bit-
map-Texturemap. Diese werden in-
tern automatisch in OGRE-Mate-
rialien konvertiert. Echte OGRE-
Materialien erstellt man mithilfe
des neuen OGRE-Materials (wel-
ches sich nochmal in Technique-
und Pass-Material aufteilt) in 3ds
Max und man kann mit diesem
auf alle Funktionen des OGRE-
Materialsystem (siehe Kapitel 4.1 auf
Seite 44) zurückgreifen. oFusion un-
Abbildung 7.4: Die OGRE-Renderansicht inner- terstützt auÿerdem Shader in HLSL
halb von 3ds Max und Cg in Echtzeit. Dabei können die
Parameter der Shader über einen Ei-
genschaftseditor angepasst werden.

Die in 3ds Max erstellten Inhalte können in für OGRE-geeignete Formate exportiert
werden. 3D-Modelle werden in das .mesh-, Materialien in das .-material-, Shader in das
.shader- und .program- und Skelettanimation in das .skeleton-Format exportiert.
Noch komfortabler exportiert man Inhalte von 3ds Max in die OGRE-Anwendung durch
den oFusion Scene Loader und dem dazugehörigen .osm-Format. Dabei wird die kom-
plette Szene in eine Datei exportiert, die von der OGRE-Anwendung durch einen Funk-
tionsaufruf geladen wird. Somit wird die Szene genauso, wie man sie in 3ds Max erstellt
hat, mit wenig Aufwand in die OGRE-Anwendung importiert.

oFusion ist für nicht kommerzielle Benutzung kostenlos. Zur kommerziellen Nutzung
werden keine öentlichen Preisinformationen zur Verfügung gestellt.

78
7.7 RenderMonkey-Shader Exporter

Der RenderMonkey-Shader Exporter9 ist eine Erweiterung zu RenderMonkey von ATI


Technologies10 .
RenderMonkey ist eine Entwicklungsumgebung für Shader, die alle Komponenten be-
reitstellt, um moderne Shader zu entwickeln. Ein Shader-Projekt besteht dabei aus dem
eigentlichen Shader-Quellcode sowie Variablen-, 3D-Modell-, Textur- und Lichtdeklara-
tionen. Der Shader-Quellcode wird über einen Texteditor erstellt und das Ergebnis kann
in Echtzeit im Vorschaufenster betrachtet werden (siehe Abbildung 7.5 auf Seite 79). Es
werden Shader in Assembler als auch in den Hochsprachen HLSL und GLSL unterstützt.
Für weitere Informationen zur Shaderentwicklung mit RenderMonkey siehe [SL04].

Abbildung 7.5: Ein Bump-Mapping Shader in RenderMonkey von ATI Technologies

Mit dem Shader Exporter kann man nun den Shader in das OGRE-Shaderformat expor-
tieren. Der Shader-Quellcode wird dabei in .source-Dateien und die Shader-Deklaration
in .program-Dateien gespeichert. Auÿerdem wird eine Materialdenition in einer .material-
Datei erstellt. Diese Materialdenition kann man nun 3D-Modellen innerhalb der OGRE-
Anwendung mit Entity::setMaterialName() zuweisen (siehe Abbildung 7.6 auf Seite
80).

9 erhältlich unter: http://ogre.cvs.sourceforge.net/ogre/ogreaddons/RmOgreExporter/


10 erhältlich unter http://www.ati.com/developer/rendermonkey/

79
(a) Shader in RenderMonkey (b) Shader innerhalb einer OGRE-Anwendung

Abbildung 7.6: Ein einfacher Specular Lighting Shader mit einer Holztextur

Der Exporter liegt in Version 2 vor und bietet neben schlankeren erzeugten Materialde-
nitionen eine Vorschau in einem OGRE-Renderfenster.

80
8 Ausblick

Die Leistungsfähigkeit moderner Grakprozessoren wird weiter steigen und der Bedarf
an interaktiven 3D-Anwendungen weiter wachsen. Bisher hat es OGRE, dank seiner en-
gagierten Entwickler und der lebendigen Community, sehr gut geschat mit der techni-
schen Entwicklung Schritt zu halten. Ein Open Source Projekt steht und fällt mit seiner
Community und auf die kommt es in Zukunft an, um OGRE weiterzuentwickeln und
auf neue Technologien abzustimmen. Wichtig wäre es, auf weitere Kooperationen mit
professionellen Entwicklern zu setzen, um OGRE auch als Markenzeichen in der breiten
Öentlichkeit zu etablieren. Der deutsche Entwickler des Adventures Ankh, Deck13,
hat bereits einen Nachfolger, Ankh - das Herz des Osiris, wieder auf OGRE-Basis
angekündigt. Auÿerdem ist gerade ein Buch, das sich mit der OGRE-Programmierung
beschäftigt, erschienen (siehe [Jun06]).

81
A Quellcode

A.1 HelloWorld-Quellcode

 Listing A.1: HelloWorld-Quellcode 


1 /*
2 // HelloWorld . cpp
3 // Einfaches OGRE - HelloWorld Beispiel
4 */
5
6 # include < Ogre .h >
7 # include < OgreErrorDialog .h >
8 # include < OgreEventListeners .h >
9 # include < OgreKeyEvent .h >
10
11
12 using namespace Ogre ;
13
14 class Keyboard : public KeyListener , public FrameListener
15 {
16 public :
17 Keyboard ();
18 virtual void keyClicked ( KeyEvent * e );
19 virtual void keyPressed ( KeyEvent * e );
20 virtual void keyReleased ( KeyEvent * e );
21 virtual bool frameEnded ( const FrameEvent & evt );
22 private :
23 bool mEscWasPressed ;
24 };
25
26 Keyboard :: Keyboard () : mEscWasPressed ( false ) {}
27
28 void Keyboard :: keyClicked ( KeyEvent * e )
29 {
30 if (e - > getKey () == KC_ESCAPE )
31 {
32 mEscWasPressed = true ;
33 e - > consume ();
34 }
35 }
36
37 void Keyboard :: keyPressed ( KeyEvent * e ) {}

82
38
39 void Keyboard :: keyReleased ( KeyEvent * e ) {}
40
41 bool Keyboard :: frameEnded ( const FrameEvent & evt )
42 {
43 return ! mEscWasPressed ;
44 }
45
46 int main ( int argc , char * argv [])
47 {
48 try
49 {
50 Root * mRoot = new Root ( " Plugins . cfg " , " Ogre . cfg " , " ogre . log " );
51
52 if (! mRoot - > showConfigDialog ())
53 return 1;
54
55 RenderWindow * mWindow = mRoot - > initialise ( true , " HelloWorld " );
56
57 ResourceGroupManager :: getSingleton (). addResourceLocation
58 ( " < Pfad zum OGRE - SDK >\\ media \\ materials \\ scripts " , " FileSystem " );
59 ResourceGroupManager :: getSingleton (). addResourceLocation
60 ( " < Pfad zum OGRE - SDK >\\ media \\ materials \\ programs " , " FileSystem " );
61 ResourceGroupManager :: getSingleton (). addResourceLocation
62 ( " < Pfad zum OGRE - SDK >\\ media \\ materials \\ textures " , " FileSystem " );
63 ResourceGroupManager :: getSingleton (). addResourceLocation
64 ( " < Pfad zum OGRE - SDK >\\ media \\ models " ," FileSystem " );
65 ResourceGroupManager :: getSingleton (). initialiseAllResourceGroups ();
66
67 SceneManager * mSceneManager = mRoot - > createSceneManager ( ST_GENERIC );
68
69 Entity * mOgreEntity = mSceneManager - > createEntity ( " OgreEntity1 " ,
70 " ogrehead . mesh " );
71
72 SceneNode * mOgreSceneNode = mSceneManager - > getRootSceneNode () - >
73 createChildSceneNode ( " Ogre1Node " , Vector3 (0 ,0 ,0));
74 mOgreSceneNode - > attachObject ( mOgreEntity );
75
76 mSceneManager - > setAmbientLight ( ColourValue (0.1 ,0.1 ,0.1 ,1));
77
78 Light * mLight = mSceneManager - > createLight ( " Light1 " );
79 mLight - > setPosition ( -80 , 80 , 200);
80
81 Camera * mCamera = mSceneManager - > createCamera ( " Camera1 " );
82 mCamera - > setNearClipDistance (5);
83 mCamera - > setPosition ( Vector3 (40 ,40 ,100));
84 mCamera - > lookAt (0 ,0 ,0);
85
86 Viewport * mViewport = mWindow - > addViewport ( mCamera );
87 mCamera - > setAspectRatio ( Real ( mViewport - > getActualWidth ()) /
88 Real ( mViewport - > getActualHeight ()));
89

83
90 Keyboard * mKeyboard = new Keyboard ();
91 mRoot - > addFrameListener ( mKeyboard );
92 EventProcessor * mEventProcessor = new EventProcessor ();
93 mEventProcessor - > addKeyListener ( mKeyboard );
94 mEventProcessor - > initialise ( mWindow );
95 mEventProcessor - > startProcessingEvents ();
96
97 mRoot - > startRendering ();
98 delete mRoot ;
99 }
100 catch ( Exception & e )
101 {
102 ErrorDialog * errorDlg =
103 PlatformManager :: getSingleton (). createErrorDialog ();
104 errorDlg - > display ( e . getFullDescription ());
105 delete errorDlg ;
106 return 1;
107 }
108 return 0;
109 }




84
A.2 Eine interaktive Anwendung-Quellcode

 Listing A.2: Eine interaktive Anwendung-Quellcode 


1 /*
2 // Ein interaktives Beispiel : Kamera - und Charaktersteuerung
3 */
4
5 # include < Ogre .h >
6 # include < OgreErrorDialog .h >
7 # include < OgreEventListeners .h >
8 # include < OgreKeyEvent .h >
9
10 using namespace Ogre ;
11
12 SceneNode * mCameraNode ;
13 Vector3 mTranslateVector = Vector3 :: ZERO ;
14 Radian mRotX = Radian (0);
15 Radian mRotY = Radian (0);
16 const Real mMoveScale = 150;
17 const Degree mRotScale = Degree (60);
18
19 SceneNode * mOgreSceneNode ;
20 AnimationState * mAnimWalk ;
21 AnimationState * mAnimIdle ;
22 Vector3 mRobotTranslate = Vector3 :: ZERO ;
23 Radian mRobotRot = Radian (0);
24
25 class Keyboard : public KeyListener , public FrameListener
26 {
27 public :
28 Keyboard ();
29 virtual void keyClicked ( KeyEvent * e );
30 virtual void keyPressed ( KeyEvent * e );
31 virtual void keyReleased ( KeyEvent * e );
32 virtual bool frameStarted ( const FrameEvent & evt );
33 virtual bool frameEnded ( const FrameEvent & evt );
34 private :
35 bool mEscWasPressed ;
36 };
37
38 Keyboard :: Keyboard () : mEscWasPressed ( false ) {}
39
40 void Keyboard :: keyClicked ( KeyEvent * e )
41 {
42 if (e - > getKey () == KC_ESCAPE )
43 {
44 mEscWasPressed = true ;
45 e - > consume ();
46 }
47 }
48

85
49 void Keyboard :: keyPressed ( KeyEvent * e )
50 {
51 if (e - > getKey () == KC_UP )
52 mTranslateVector . z -= mMoveScale ;
53 if (e - > getKey () == KC_DOWN )
54 mTranslateVector . z += mMoveScale ;
55
56 if (e - > getKey () == KC_LEFT )
57 mTranslateVector . x -= mMoveScale ;
58 if (e - > getKey () == KC_RIGHT )
59 mTranslateVector . x += mMoveScale ;
60
61 if (e - > getKey () == KC_PGUP )
62 mTranslateVector . y += mMoveScale ;
63 if (e - > getKey () == KC_PGDOWN )
64 mTranslateVector . y -= mMoveScale ;
65
66 if (e - > getKey () == KC_W )
67 mRotY -= mRotScale ;
68 if (e - > getKey () == KC_S )
69 mRotY += mRotScale ;
70
71 if (e - > getKey () == KC_A )
72 mRotX += mRotScale ;
73 if (e - > getKey () == KC_D )
74 mRotX -= mRotScale ;
75
76 if (e - > getKey ()== KC_NUMPAD8 )
77 {
78 mRobotTranslate . x += mMoveScale / 4;
79 mAnimIdle - > setEnabled ( false );
80 mAnimWalk - > setEnabled ( true );
81 }
82 if (e - > getKey ()== KC_NUMPAD2 )
83 {
84 mRobotTranslate . x -= mMoveScale / 4;
85 mAnimIdle - > setEnabled ( false );
86 mAnimWalk - > setEnabled ( true );
87 }
88 if (e - > getKey ()== KC_NUMPAD4 )
89 {
90 mRobotRot += mRotScale ;
91 mAnimIdle - > setEnabled ( false );
92 mAnimWalk - > setEnabled ( true );
93 }
94 if (e - > getKey ()== KC_NUMPAD6 )
95 {
96 mRobotRot -= mRotScale ;
97 mAnimIdle - > setEnabled ( false );
98 mAnimWalk - > setEnabled ( true );
99 }
100 }

86
101
102 void Keyboard :: keyReleased ( KeyEvent * e )
103 {
104 if (e - > getKey () == KC_UP )
105 mTranslateVector . z += mMoveScale ;
106 if (e - > getKey () == KC_DOWN )
107 mTranslateVector . z -= mMoveScale ;
108
109 if (e - > getKey () == KC_LEFT )
110 mTranslateVector . x += mMoveScale ;
111 if (e - > getKey () == KC_RIGHT )
112 mTranslateVector . x -= mMoveScale ;
113
114 if (e - > getKey () == KC_PGUP )
115 mTranslateVector . y -= mMoveScale ;
116 if (e - > getKey () == KC_PGDOWN )
117 mTranslateVector . y += mMoveScale ;
118
119 if (e - > getKey () == KC_W )
120 mRotY += mRotScale ;
121 if (e - > getKey () == KC_S )
122 mRotY -= mRotScale ;
123
124 if (e - > getKey () == KC_A )
125 mRotX -= mRotScale ;
126 if (e - > getKey () == KC_D )
127 mRotX += mRotScale ;
128
129 if (e - > getKey ()== KC_NUMPAD8 )
130 {
131 mRobotTranslate . x -= mMoveScale / 4;
132 mAnimIdle - > setEnabled ( true );
133 mAnimWalk - > setEnabled ( false );
134 }
135 if (e - > getKey ()== KC_NUMPAD2 )
136 {
137 mRobotTranslate . x += mMoveScale / 4;
138 mAnimIdle - > setEnabled ( true );
139 mAnimWalk - > setEnabled ( false );
140 }
141 if (e - > getKey ()== KC_NUMPAD4 )
142 {
143 mRobotRot -= mRotScale ;
144 mAnimIdle - > setEnabled ( true );
145 mAnimWalk - > setEnabled ( false );
146 }
147 if (e - > getKey ()== KC_NUMPAD6 )
148 {
149 mRobotRot += mRotScale ;
150 mAnimIdle - > setEnabled ( true );
151 mAnimWalk - > setEnabled ( false );
152 }

87
153 }
154
155 bool Keyboard :: frameStarted ( const FrameEvent & evt )
156 {
157 mAnimIdle - > addTime ( evt . timeSinceLastFrame );
158 mAnimWalk - > addTime ( evt . timeSinceLastFrame );
159
160 mOgreSceneNode - > yaw ( mRobotRot * evt . timeSinceLastFrame );
161 mOgreSceneNode - > translate ( mOgreSceneNode - > getOrientation () *
162 mRobotTranslate * evt . timeSinceLastFrame );
163
164 mCameraNode - > yaw ( mRotX * evt . timeSinceLastFrame );
165 mCameraNode - > pitch ( mRotY * evt . timeSinceLastFrame );
166 mCameraNode - > translate ( mCameraNode - > getOrientation () *
167 mTranslateVector * evt . timeSinceLastFrame );
168
169 return true ;
170 }
171
172 bool Keyboard :: frameEnded ( const FrameEvent & evt )
173 {
174 return ! mEscWasPressed ;
175 }
176
177 int main ( int argc , char * argv [])
178 {
179 try
180 {
181 Root * mRoot = new Root ( " Plugins . cfg " , " Ogre . cfg " , " ogre . log " );
182
183 if (! mRoot - > showConfigDialog ())
184 return 1;
185
186 RenderWindow * mWindow = mRoot - > initialise ( true ,
187 " Interaktive Anwendung " );
188
189 ResourceGroupManager :: getSingleton (). addResourceLocation
190 ( " C :\\ Programmieren \\ OgreSDK \\ media \\ materials \\ scripts " ,
191 " FileSystem " );
192 ResourceGroupManager :: getSingleton (). addResourceLocation
193 ( " C :\\ Programmieren \\ OgreSDK \\ media \\ materials \\ programs " ,
194 " FileSystem " );
195 ResourceGroupManager :: getSingleton (). addResourceLocation
196 ( " C :\\ Programmieren \\ OgreSDK \\ media \\ materials \\ textures " ,
197 " FileSystem " );
198 ResourceGroupManager :: getSingleton (). addResourceLocation
199 ( " C :\\ Programmieren \\ OgreSDK \\ media \\ models " ," FileSystem " );
200 ResourceGroupManager :: getSingleton (). initialiseAllResourceGroups ();
201
202 SceneManager * mSceneManager =
203 mRoot - > createSceneManager ( ST_GENERIC );
204

88
205 Entity * mOgreEntity = mSceneManager - > createEntity ( " OgreEntity1 " ,
206 " robot . mesh " );
207
208 mOgreSceneNode = mSceneManager - > getRootSceneNode () - >
209 createChildSceneNode ( " Ogre1Node " , Vector3 (0 ,0 ,0));
210 mOgreSceneNode - > attachObject ( mOgreEntity );
211 mOgreSceneNode - > setScale (0.5 , 0.5 , 0.5);
212
213 mAnimWalk = mOgreEntity - > getAnimationState ( " Walk " );
214 mAnimWalk - > setLoop ( true );
215 mAnimIdle = mOgreEntity - > getAnimationState ( " Idle " );
216 mAnimIdle - > setLoop ( true );
217 mAnimIdle - > setEnabled ( true );
218
219 mSceneManager - > setAmbientLight ( ColourValue (0.1 ,0.1 ,0.1 ,1));
220
221 Light * mLight = mSceneManager - > createLight ( " Light1 " );
222 mLight - > setPosition ( -80 , 80 , 200);
223
224 Camera * mCamera = mSceneManager - > createCamera ( " Camera1 " );
225 mCamera - > setNearClipDistance (5);
226 mCameraNode = mSceneManager - > getRootSceneNode () - >
227 createChildSceneNode ( " CameraNode " , Vector3 (0 , 0 , 0));
228 mCameraNode - > attachObject ( mCamera );
229 mCameraNode - > translate (40 , 40 , 100);
230 mCameraNode - > setDirection ( - mCameraNode - > getPosition ());
231
232 Viewport * mViewport = mWindow - > addViewport ( mCamera );
233 mCamera - > setAspectRatio ( Real ( mViewport - > getActualWidth ()) /
234 Real ( mViewport - > getActualHeight ()));
235
236 Keyboard * mKeyboard = new Keyboard ();
237 mRoot - > addFrameListener ( mKeyboard );
238 EventProcessor * mEventProcessor = new EventProcessor ();
239 mEventProcessor - > addKeyListener ( mKeyboard );
240 mEventProcessor - > initialise ( mWindow );
241 mEventProcessor - > startProcessingEvents ();
242
243 mRoot - > startRendering ();
244 delete mRoot ;
245 }
246 catch ( Exception & e )
247 {
248 ErrorDialog * errorDlg =
249 PlatformManager :: getSingleton (). createErrorDialog ();
250 errorDlg - > display ( e . getFullDescription ());
251 delete errorDlg ;
252 return 1;
253 }
254 return 0;
255 }




89
A.3 Compositor: Weichzeichner-Materialskript

 Listing A.3: SimpleBlur-Materialskript 


1 // Pixelshader - Deklaration
2 fragment_program SimpleBlur . Pixel_Shader hlsl
3 {
4 source VDS . ScreenSpace . SimpleBlur . Pixel_Shader . source
5 target ps_2_0
6 entry_point ps_main
7
8 default_params
9 {
10 param_named_auto viewport_inv_width inverse_viewport_width
11 param_named_auto viewport_inv_height inverse_viewport_height
12 }
13 }
14
15 // Effekt : Ein einfacher Blur - Filter
16 material ScreenSpace . SimpleBlur
17 {
18 technique
19 {
20 pass
21 {
22 depth_check off
23
24 // Verweis auf den Pixelshader
25 fragment_program_ref SimpleBlur . Pixel_Shader
26 {
27 }
28
29 // Übergabe der Textur
30 texture_unit Texture0
31 {
32 tex_coord_set 0
33 tex_address_mode clamp
34 filtering linear linear linear
35
36 }
37 }
38 }
39 }




90
A.4 Compositor: Weichzeichner-Pixelshader

 Listing A.4: SimpleBlur-Pixelshader 


1 float viewport_inv_width ;
2 float viewport_inv_height ;
3
4 // Das gerenderte Bild in einer Textur
5 sampler Texture0 : register ( s0 );
6
7 float4 ps_main ( float2 texCoord : TEXCOORD0 ) : COLOR
8 {
9 // Filter - Kernerl
10 float4 samples [4] = {
11 -1.0 , 0.0 , 0, 0.25 ,
12 1.0 , 0.0 , 0, 0.25 ,
13 0.0 , 1.0 , 0, 0.25 ,
14 0.0 , -1.0 , 0, 0.25
15 };
16
17 float4 col = float4 (0 ,0 ,0 ,0);
18
19 // Wende einen Box - Filter auf das Pixel an
20 for ( int i =0; i <4; i ++)
21 col += samples [ i ]. w * tex2D ( Texture0 , texCoord +
22 float2 ( samples [ i ]. x * viewport_inv_width ,
23 samples [ i ]. x * viewport_inv_height ));
24
25 return col ;
26 }




91
A.5 Beispielshader: Vertexshader

 Listing A.5: Vertexshader des Beispielshaders 


1 struct VS_INPUT
2 {
3 float4 Pos : POSITION ;
4 float3 Normal : NORMAL ;
5 float2 Txr : TEXCOORD0 ;
6 };
7
8 struct VS_OUTPUT
9 {
10 float4 Pos : POSITION ;
11 float2 Txr : TEXCOORD0 ;
12 float4 Color : COLOR0 ;
13 };
14
15 float4x4 matViewProjection ; // Transformationsmatrix
16 float4 viewPosition ; // Die Position des Betrachters
17 float4 LightAmbient ; // Das ambiente Licht
18 float4 LightPos ; // Position der Lichtquelle
19 float4 LightColor ; // Farbe der Lichtquelle
20
21 float4 Light_PointDiffuse ( float3 VertPos , float3 VertNorm ,
22 float3 LightPos , float4 LightColor )
23 {
24 // Berechne die Richtung aus der das Licht auf den Vertex trifft und
25 // normalisiere diesen Vektor
26 float3 LightDir = normalize ( LightPos - VertPos );
27
28 // Berechne die Lichtintensität bezüglich des Winkels zwischen
29 // der Lichtrichtung und der Vertex - Normalen über das Punktprodukt
30 float AngleAttn = dot ( VertNorm , LightDir );
31
32 // Da dieser Wert negativ werden kann , muss er auf den Wertebereich
33 // [0 , 1] beschnitten werden
34 AngleAttn = clamp (0 , 1 , AngleAttn );
35
36 // Berechne finale Beleuchtungsintensität
37 return LightColor * AngleAttn ;
38 }
39
40 VS_OUTPUT vs_main ( VS_INPUT In )
41 {
42 VS_OUTPUT Out ;
43
44 // Berechne die in den Bildschirmraum projizierte Position des Vertex
45 Out . Pos = mul ( matViewProjection , In . Pos );
46
47 // Leite Texturkoordinaten unverändert weiter
48 Out . Txr = In . Txr ;

92
49
50 // Das ambiente Licht definiert den Grundanteil der Lichtintensität
51 float4 Color = LightAmbient ;
52
53 // Berechne Lichtintensität durch die zuvor definierte Funktion
54 Color += Light_PointDiffuse ( In . Pos , In . Normal ,
55 LightPos , LightColor );
56
57 // Gib berechnete Farbe an den Pixelshader weiter
58 Out . Color = Color ;
59
60 return Out ;
61 }




A.6 Beispielshader: Pixelshader

 Listing A.6: Pixelshader des Beispielshaders 


1 struct PS_INPUT
2 {
3 float2 Txr : TEXCOORD0 ;
4 float4 Color : COLOR0 ;
5 };
6
7 sampler Texture0 ;
8
9 float4 ps_main ( PS_INPUT In )
10 {
11 // Die endgültige Farbe des aktuellen Pixels ergibt sich aus
12 // der interpolierten Lichtintensität , die im Vertexshader
13 // berechnet wurde multipliziert mit dem Farbwert der Textur
14 float4 finalColor = In . Color * tex2D ( Texture0 , In . Txr );
15
16 // Die berechnete Farbe wird ausgegeben
17 return finalColor ;
18 }




93
B Inhalt der Begleit-CD

Im Ordner Ausarbeitung bendet sich diese Bachelorarbeit als .pdf-Datei (OGRE3D,


Bachelor, Lars Bilke.pdf ).
Im Ordner Beispiele bendet sich die Visual Studio Projektdatei der in der Arbeit be-
sprochenen Beispielprogramme. Die ausführbaren Dateien liegen im Unterordner debug
und release Um die Beispiele auszuprobieren, muss das Beispiele-Verzeichnis erst auf
Festplatte kopiert werden, da OGRE Schreibzugri auf einige Dateien benötigt..
Der Ordner OGRE3D enthält das OGRE-SDK und den OGRE-Quellcode der Version
1.2.2.
Nützliche Tools für OGRE sind im Ordner Toolszu nden.

94
Literaturverzeichnis

[Dev06] DevMaster.net: Vergleich von 3D-Engines. URL: http://www.


devmaster.net/engines, September 2006.
[Eng05] Engel, Wolfgang: ShaderX3. ShaderX Series. Charles River Media, 2005.
[Eri05] Ericson, Christer : Real-Time Collision Detection. Series In Interactive
3D Technology. Morgan Kaufmann Publishers, 2005.
[Fer04] Fernando, Randima: GPU Gems. Pearson Education, Inc., September
2004.
[Gar06] GarageGames.com: Homepage der 3D-Engine Torque. URL: http://www.
garagegames.com, September 2006.
[Irr06] Irrlicht.Sourceforge.net/: Homepage der 3D-Engine Irrlicht. URL:
http://irrlicht.sourceforge.net/, September 2006.
[Jun06] Junker, Gregory: Pro OGRE 3D Programming. Springer-Verlag New
York, 2006.
[Mot03] Mothe, André La : Tricks Of The 3D Game Programming Gurus - Advanced
3D Graphics And Rasterization. Sams Publishing, Juni 2003.
[Pan06] Panda3D.org : Homepage der 3D-Engine Panda3D. URL: http://www.
panda3d.org, September 2006.
[Rad06] RadonLabs.de: Homepage der 3D-Engine Nebula Device. URL: http:
//www.radonlabs.de, September 2006.
[Ros04] Rost, Randi J.: OpenGL Shading Language. Pearson Education, Inc., 2004.
[Sch03] Scherfgen, David : 3D-Spieleprogrammierung - Modernes Game Design mit
DirectX 9 und C++. Carl Hanser Verlag München, 2003.
[Sch06] Schürmann, Tim : Spieleentwicklung mit OGRE. GameStar/dev, Mai 2006.
[SL04] St-Laurent, Sebastian : Shaders For Game Programmers And Artists.
Thomson Course Technology PTR, 2004.
[The06a] The OGRE Team: Ogre api reference v1.2.2, Juli 2006.
[The06b] The OGRE Team : Ogre manual v1.2.2 ('dagon'), Juli 2006.
[Unr06] Unrealtechnology.com : Homepage der Unreal-Engine. URL: http://
unrealtechnology.com/, September 2006.

95
[Val06] ValveSoftware.com: Engine Licensing Information Sheet. URL: http:
//www.valvesoftware.com, September 2006.
[Wik06] Wikipedia, Online-Enzyklopädie: Diverse Artikel. URL: http://www.
wikipedia.org, September 2006.

96

Das könnte Ihnen auch gefallen