Um ein 3D-Modell mit der Murl Engine laden zu können, muss dieses zuerst in eine Resource::Mesh
umgewandelt werden. Die Murl Engine stellt dafür die beiden Tools Scene Converter und Scene Viewer zur Verfügung. Üblicherweise sind folgende Schritte notwendig, um ein 3D-Modell in einer App verwenden zu können:
- Export des 3D-Modells aus der 3D-Grafiksoftware
- Analyse des 3D-Modells
- Konvertierung des 3D-Modells
- Instanzieren des konvertierten 3D-Modells
Quicklinks zu den einzelnen Abschnitten in diesem Tutorial:
- Export
- Analyse
- Konfigurationsdatei
- Paket laden und 3D-Modell anzeigen
- Material ersetzen
- Mehrere Instanzen
- Messerschmitt Bf 110
- Darius
- Pony
- Nützliche Konfigurationsparameter
Export
3D-Modelle können in jeder beliebigen 3D-Grafiksoftware (z.B. Blender, Cinema 4d, 3ds Max, zBrush etc.) erstellt werden. Für den Scene Converter muss das Modell in einem geeigneten Dateiformat exportiert werden.
Der Scene Converter verwendet Assimp (Open Asset Import Library) bzw. das Autodesk FBX SDK zum Laden der Modelle und kommt daher mit vielen Dateiformaten zurecht. Eine Liste der unterstützen Assimp Formate findet sich hier.
Wir empfehlen das FBX Fileformat zu verwenden, da es flexibel ist und es damit üblicherweise wenige Probleme beim Konvertieren gibt.
Analyse
Für dieses Tutorial verwenden wir einige freie Modelle von der Website sketchfab.com.
Das erste Modell nennt sich Easter Island Head, wurde von Microsoft erstellt und steht unter der Creative Commons Attribution Lizenz. Die Datei easter-island-head.stl
speichern wir im Ordner data/orig/easter-island-head
.
Das Scene Converter Tool kann mit dem Parameter -z
für die Analyse der Datei verwendet werden.
Das Tool liefert folgenden Output:
Murl Scene Converter V1.00.5467 beta, Copyright 2010-2015 Spraylight GmbH Scene Converter: Analyzing scene from file 'easter-island-head\easter-island-head.stl' Scene Converter: Loading scene file 'easter-island-head\easter-island-head.stl'. Scene Converter: Parsing textures. Scene Converter: Parsing materials. Scene Converter: Parsing animations. Scene Converter: Parsing meshes. Scene Converter: Parsing nodes. Scene Converter: Scene info: Scene Converter: Number of textures: 0 Scene Converter: Number of materials: 1 Scene Converter: Material 0000: uc=0 name="DefaultMaterial" ambientColor=ff0c0c0c diffuseColor=ff999999 specularColor=ff999999 emissiveColor=00000000 shininess=4.000000 numTextures=0 Scene Converter: Number of animations: 1 Scene Converter: Animation 0000: uc=0 name="Anim0" Scene Converter: Number of bones: 0 Scene Converter: Number of meshes: 1 Scene Converter: Mesh 0000: uc=0 name="Mesh0" materialName="DefaultMaterial" numFaces=11176 numVertices=26980 numBones=0 nm=0 nc=0 Scene Converter: Number of nodes: 2 Scene Converter: Node 0000: uc=0 name="" numChildren=1 numMeshes=0 numBones=0 Scene Converter: Child 0000: index=0001 name="x3cSTL_BINARYx3e" Scene Converter: Node 0001: uc=0 name="x3cSTL_BINARYx3e" numChildren=0 numMeshes=1 numBones=0 Scene Converter: Mesh 0000: index=0000 name="Mesh0" Scene Converter: Skeleton Graph: Scene Converter: Model Graph: Scene Converter: Node 0000: name="" numMeshes=0 Scene Converter: Node 0000: name="" numMeshes=0 Scene Converter: Node 0001: name="x3cSTL_BINARYx3e" numMeshes=1 Scene Converter: Mesh 0000: index=0000 name="Mesh0"
Wir sehen, dass das Modell keine Texturen verwendet, lediglich ein Material hat und aus 26980 Eckpunkten und 11176 Flächen besteht.
Konfigurationsdatei
Nun können wir das Modell konvertieren.
Das Scene Converter Tool wird (wie auch das Atlas Generator Tool) über eine XML-Konfigurationsdatei konfiguriert. Als Minimum müssen wir die InputScene
und das zu erstellende OutputPackage
angeben:
<?xml version="1.0"?> <!-- Copyright 2015 Spraylight GmbH --> <SceneConverter xmlns="https://murlengine.com"> <InputScene fileName="easter-island-head/easter-island-head.stl"> </InputScene> <OutputPackage fileName="../packages/model3d" packageId="model3d" createFolders="yes"> <GraphInstance graphId="GraphInstance" filePrefix="graph_instance" generateMeshes="yes"> </GraphInstance> </OutputPackage> </SceneConverter>
Mit dem Parameter -c
können wir die Konfigurationsdatei angeben. Wird zusätzlich der Parameter -lv
angegeben, so wird das Ergebnis gleich im Scene Viewer angezeigt:
Die Navigation im Scene Viewer erfolgt über die Cursor-Tasten in Kombination mit der Shift-Taste und der Alt-Taste. Über die Tastatur können auch verschiedene Elemente ein- und ausgeschalten werden. Beispielsweise kann mit der Taste B die Bounding-Box des Modells angezeigt werden, mit S die Skybox umgeschaltet werden usw. Infos über die genaue Tastenbelegung und die genauen Koordinaten der Bounding-Box werden immer rechts oben angezeigt.
Wir können in der XML Konfigurationsdatei weitere Angaben machen und damit das Modell direkt bei der Konvertierung für unsere Zwecke anpassen (z.B. mit translateX/Y/Z
, rotateX/Y/Z
, scale
). Dafür verallgemeinern wir die XML-Konfigurationsdatei etwas und setzen die Parameter über selbst definierte Attribute:
<?xml version="1.0"?> <!-- Copyright 2015 Spraylight GmbH --> <SceneConverter xmlns="https://murlengine.com" sceneId="Scene" translateX="0" translateY="0" translateZ="0" rotateX="0d" rotateY="0d" rotateZ="0d" scale="1" outPath="../packages/" packageName="model3d"> <InputScene id="{sceneId}" fileName="{inputName}" translateX="{translateX}" translateY="{translateY}" translateZ="{translateZ}" rotateX="{rotateX}" rotateY="{rotateY}" rotateZ="{rotateZ}" scale="{scale}"> </InputScene> <OutputPackage fileName="{outPath}{packageName}" packageId="{packageName}" createFolders="yes"> <GraphInstance graphId="GraphInstance" filePrefix="graph_instance" generateMeshes="yes"> </GraphInstance> </OutputPackage> </SceneConverter>
Wir setzten die Attribute mit dem Paramter -a
und verwenden rotateX
um das 3D-Modell zu drehen und translateZ
um das Modell in den Mittelpunkt der X/Z-Ebene zu schieben.
Mit diesen Änderungen wird das konvertierte Modell nun richtig ausgerichtet.
Paket laden und 3D-Modell anzeigen
Das erzeugte Paket liegt, wie in der XML-Konfigurationsdatei angegeben, unter ../packages/model3d.murlres
. Dieses Paket können wir wie jedes andere Ressourcen Paket in unserem App-Code laden.
loader->AddPackage("model3d", ILoader::LOAD_MODE_BACKGROUND);
Um das 3D-Modell anzuzeigen, müssen wir es im Szenengraphen instanzieren. Wie in der XML-Konfigurationsdatei angegeben, lautet der Ressourcen-Paket-Name model3d
und die id
des 3D-Modells lautet GraphInstance:
<Instance graphResourceId="/model3d/GraphInstance"/>
Damit das 3D-Modell richtig gerendert wird, müssen wir auch noch eine Lichtquelle definieren:
<Light id="light01" type="DIRECTIONAL"/> <LightState lightId="light01"/>
Als Ergebnis wird das 3D-Modell wie gewünscht in der Szene angezeigt.
Material ersetzen
Bei genauer Analyse des erzeugten Ressourcen-Pakets, sehen wir, dass im Paket ein eigenes Material erzeugt und instanziert wird:
<?xml version="1.0" ?> <Graph> <Namespace id="Materials" activeAndVisible="NO"> <!-- DefaultMaterial --> <FixedProgram id="FixedProgram0" lightingEnabled="yes" coloringEnabled="yes"/> <Material id="Material0" programId="FixedProgram0"/> </Namespace> </Graph>
Wir können dieses Material aber auch durch ein eigenes Material ersetzen. Dafür passen wir die Konfigurationsdatei an und ersetzen alle Materialien mit unserem Material /myMaterial/mat_color_light_alpha
und geben auch noch Werte für diffuseColor
und ambientColor
an.
Wenn wir nun den Scene Converter starten, wird das Modell in der Applikation mit unserem Material angezeigt. Allerdings wird jetzt im Scene Viewer gar kein 3D-Modell mehr angezeigt. Das ist deshalb so, weil der Scene Viewer unser Material /myMaterial/mat_color_light_alpha
nicht kennt. Wir müssen also für den Scene Viewer auch unser Ressourcenpaket mit den Materialien in der Konfigurationsdatei mit InputPackage
angeben:
<?xml version="1.0"?> <!-- Copyright 2015 Spraylight GmbH --> <SceneConverter xmlns="https://murlengine.com" sceneId="Scene" ignoreFiles="no" translateX="0" translateY="0" translateZ="0" rotateX="0d" rotateY="0d" rotateZ="0d" scale="1" outPath="../packages/" packageName="model3d"> <InputPackage fileName="../packages/materials.murlres"/> <InputScene id="{sceneId}" fileName="{inputName}" translateX="{translateX}" translateY="{translateY}" translateZ="{translateZ}" rotateX="{rotateX}" rotateY="{rotateY}" rotateZ="{rotateZ}" scale="{scale}"> <Material findIdMatchingPattern="*" replaceWithGraphNodeId="/myMaterial/mat_color_light" diffuseColor="1f,0f,0f,1.0f" ambientColor="0f,0f,1f,1.0f" /> </InputScene> <OutputPackage fileName="{outPath}{packageName}" packageId="{packageName}" createFolders="yes"> <GraphInstance graphId="GraphInstance" filePrefix="graph_instance" generateMeshes="yes"> </GraphInstance> </OutputPackage> </SceneConverter>
Nun wird das 3D-Modell auch im Scene Viewer mit unserem Material dargestellt:
- Zu beachten
- Tipp! Es empfiehlt sich die Materialien in ein eigenes Ressourcen-Paket zu geben, damit diese problemlos sowohl im Scene Viewer als auch in der Applikation verwendet werden können.
Mehrere Instanzen
Wenn mehrere Instanzen eines 3D-Modells (bzw. verschiedener 3D-Modelle) instanziert werden sollen, kann es schnell zu Namenskonflikten kommen. Um solche Konflikte zu vermeiden, kann und sollte bei der Instanzierung immer eine namespaceId
mit angegeben werden:
<Instance graphResourceId="model3d:GraphInstance" namespaceId="easter_island" />
Neben dem namespaceId
Attribut können auch noch weitere Attribute angegeben werden (siehe model3d.murlres/graph/graph_instance.xml
):
<Instance graphResourceId="model3d:GraphInstance" namespaceId="easter_island" useAnimations="YES" useMaterials="YES" useParameters="YES" useTextures="YES" useSurfaces="YES" useModel="YES" />
Nachfolgend werden beispielhaft noch ein paar weitere 3D-Modelle konvertiert und die dabei notwendigen Konfigurationsparameter erläutert.
Messerschmitt Bf 110
Dieses Modell nennt sich Messerschmitt Bf 110, wurde von helijah erstellt und steht unter der Creative Commons Attribution Lizenz. Im Ziparchiv befinden sich das 3D-Modell im OBJ-Format sowie verschiedene Texturen im PNG-Format.
Beim Konvertieren ohne weitere Angaben erhalten wir eine Fehlermeldung, da die Textur-Bilder nicht gefunden wurden:
Murl Scene Converter V1.00.5467 beta, Copyright 2010-2015 Spraylight GmbH Scene Converter: Processing XML file "scene_config_bf110.xml" Scene Converter: Importing scene from file 'bf110/bf110.obj'. Scene Converter: Loading scene file 'bf110/bf110.obj'. Scene Converter: Parsing textures. Scene Converter: Parsing materials. Scene Converter: Creating material texture for type 1 from file 'texture.png'. Scene Converter: Error Failed to load texture file 'texture.png' for texture 'texture.png' Scene Converter: Error Failed to parse materials from file 'bf110/bf110.obj'. Scene Converter: Error Failed to import file 'bf110/bf110.obj'.
Mit der Angabe einer ImageLocation
lässt sich das Modell problemlos konvertieren:
<?xml version="1.0"?> <!-- Copyright 2015 Spraylight GmbH --> <SceneConverter xmlns="https://murlengine.com" sceneId="Scene" ignoreFiles="no" translateX="0" translateY="0" translateZ="0" rotateX="0d" rotateY="0d" rotateZ="0d" scale="1" outPath="../packages/" packageName="model3d"> <InputScene id="{sceneId}" fileName="{inputName}" translateX="{translateX}" translateY="{translateY}" translateZ="{translateZ}" rotateX="{rotateX}" rotateY="{rotateY}" rotateZ="{rotateZ}" scale="{scale}"> <ImageLocation pathName="./bf110"/> </InputScene> <OutputPackage fileName="{outPath}{packageName}" packageId="{packageName}" createFolders="yes"> <GraphInstance graphId="GraphInstance" filePrefix="graph_instance" generateMeshes="yes"> </GraphInstance> </OutputPackage> </SceneConverter>
Messerschmitt Bf 110 Multipass
Wir können auch bei diesem Modell das Material durch ein eigenes Material ersetzen. Allerdings benötigen wir mehrere Materialien, da das Modell sowohl texturierte Geometrien als auch transparente Geometrien für das Glasverdeck verwendet.
Wir analysieren das Modell um die Namen der Materialien zu bestimmen:
... Scene Converter: Number of materials: 21 Scene Converter: Material 0000: uc=0 name="DefaultWhite" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0000 name="texture.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0001: uc=0 name="DefaultWhite_ai.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0001 name="ai.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0002: uc=0 name="DefaultWhite_airspeed.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0002 name="airspeed.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0003: uc=0 name="DefaultWhite_alt.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0003 name="alt.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0004: uc=0 name="DefaultWhite_compass.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0004 name="compass.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0005: uc=0 name="DefaultWhite_compass2.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0005 name="compass2.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0006: uc=0 name="DefaultWhite_fuel.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0006 name="fuel.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0007: uc=0 name="DefaultWhite_gear.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0007 name="gear.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0008: uc=0 name="DefaultWhite_guns.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0008 name="guns.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0009: uc=0 name="DefaultWhite_interior.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0009 name="interior.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0010: uc=0 name="DefaultWhite_jauges.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0010 name="jauges.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0011: uc=0 name="DefaultWhite_man.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0011 name="man.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0012: uc=0 name="DefaultWhite_manette.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0012 name="manette.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0013: uc=0 name="DefaultWhite_manettes.png.001" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0013 name="manettes.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0014: uc=0 name="DefaultWhite_panel.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0014 name="panel.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0015: uc=0 name="DefaultWhite_pedals.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0015 name="pedals.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0016: uc=0 name="DefaultWhite_throttle.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0016 name="throttle.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0017: uc=0 name="DefaultWhite_turn.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0017 name="turn.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0018: uc=0 name="DefaultWhite_vsi.png" ambientColor=ff000000 diffuseColor=ffcccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=1 Scene Converter: Texture 0000: index=0018 name="vsi.png" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE" Scene Converter: Material 0019: uc=0 name="transparent" ambientColor=ff000000 diffuseColor=26cccccc specularColor=ff7f7f7f emissiveColor=ff000000 shininess=384.313721 numTextures=0 Scene Converter: Material 0020: uc=0 name="transparentExt" ambientColor=ff000000 diffuseColor=4c66cccc specularColor=ff7fffff emissiveColor=ff000000 shininess=384.313721 numTextures=0 ...
Offensichtlich gibt es 21 verschiedene Materialien, wobei lediglich die Materialien transparent und transparentExt keine Texturen verwenden. Wir ersetzen also diese zwei Materialien mit mat_color_alpha
und alle anderen mit mat_color_light_texture
.
<?xml version="1.0"?> <!-- Copyright 2015 Spraylight GmbH --> <SceneConverter xmlns="https://murlengine.com" sceneId="Scene" ignoreFiles="no" translateX="0" translateY="0" translateZ="0" rotateX="0d" rotateY="0d" rotateZ="0d" scale="1" outPath="../packages/" packageName="model3d"> <InputPackage fileName="../packages/materials.murlres"/> <InputScene id="{sceneId}" fileName="{inputName}" translateX="{translateX}" translateY="{translateY}" translateZ="{translateZ}" rotateX="{rotateX}" rotateY="{rotateY}" rotateZ="{rotateZ}" scale="{scale}"> <ImageLocation pathName="./bf110"/> <Material findIdMatchingPattern="*" replaceWithGraphNodeId="/myMaterial/mat_color_light_texture" /> <Material findIdMatchingPattern="transparent*" replaceWithGraphNodeId="/myMaterial/mat_color_alpha" ambientColor="0.5f,0f,0f,0f" /> </InputScene> <OutputPackage fileName="{outPath}{packageName}" packageId="{packageName}" createFolders="yes"> <GraphInstance graphId="GraphInstance" filePrefix="graph_instance" generateMeshes="yes"> </GraphInstance> </OutputPackage> </SceneConverter>
Das Flugzeug wird nun mit unseren Materialien gezeichnet:
Das Ergebnis scheint auf den ersten Blick einwandfrei zu sein. Sowohl die texturierten Flächen als auch die transparenten Flächen werden mit dem richtigen Material gezeichnet.
Bei genauer Betrachtung können wir aber ein Problem erkennen:
Offenbar werden in dieser Ansicht zwei Fenster etwas dunkler gezeichnet als die anderen. Die Ursache liegt in der Reihenfolge, in der die Flächen gezeichnet werden und an dem aktivierten Depth-Buffer-Mode des transparenten Materials (depthBufferMode="READ_AND_WRITE"
). Wird ein transparentes Fenster gezeichnet, so werden danach keine Flächen mehr gezeichnet, die dahinter liegen. Um transparente Flächen korrekt darstellen zu können, müssen diese in richtiger Reihenfolge (sortiert von hinten nach vorne) gezeichnet werden (siehe auch Order Independent Transparency)
Um das Ergebnis zu verbessern, können wir den Depth-Buffer-Mode für das transparente Material auf "READ_ONLY"
zu setzten. Dadurch werden zuerst alle opaquen Geometrien mit depthBufferMode="READ_AND_WRITE"
gezeichnet und dann die transparente Geometrie mit depthBufferMode="READ_ONLY"
(siehe auch Tutorial #14: Zeichenreihenfolge).
Das funktioniert gut, da alle transparenten Flächen mit der gleichen Farbe (ohne Berücksichtigung des Lichteinfalls) gezeichnet werden.
Soll auch das Licht berücksichtig werden, kann die transparente Geometrie mit einem Graph::MultiMaterial
gerendert werden. Dabei wird die transparente Geometrie zwei Mal gerendert. Im ersten Durchgang werden nur Flächen die von der Camera wegschauen gezeichnet (visibleFaces="BACK_ONLY"
) und im zweiten Durchgang die Flächen, die zur Camera schauen (visibleFaces="FRONT_ONLY"
). Dafür definieren wir zwei Materialien, bestimmen mit sortOrder
die Zeichenreihenfolge und kombinieren beide Materialien zu einem Graph::MultiMaterial
.
<Material id="mat_color_light_alpha_back" programId="prg_color_light" visibleFaces="BACK_ONLY" blendMode="ALPHA" depthBufferMode="READ_ONLY" sortOrder="1" /> <Material id="mat_color_light_alpha_front" programId="prg_color_light" visibleFaces="FRONT_ONLY" blendMode="ALPHA" depthBufferMode="READ_ONLY" sortOrder="2" /> <MultiMaterial id="mat_color_light_alpha_twopass" materialIds="mat_color_light_alpha_back,mat_color_light_alpha_front" />
Dieses Graph::MultiMaterial
verwenden wir dann für das Rendern der transparenten Geometrie. Zusätzlich können wir noch mit ambientColor
den Ambient-Farbwert zu einem bläulichen Farbwert abändern.
<Material findIdMatchingPattern="transparent*" replaceWithGraphNodeId="/myMaterial/mat_color_light_alpha_twopass" ambientColor="0.0f,0f,0.5f,1f" />
Darius
Dieses Modell nennt sich Darius, wurde von kraken erstellt und steht unter der Creative Commons Attribution Lizenz. Im Ziparchiv befinden sich das 3D-Modell im OBJ-Format sowie eine Textur im TGA-Format.
Eine Analyse mit dem Scene Converter liefert folgendes Ergebnis:
Murl Scene Converter V1.00.5534 beta, Copyright 2010-2015 Spraylight GmbH Scene Converter: Analyzing scene from file 'darius\Darius.obj' Scene Converter: Loading scene file 'darius\Darius.obj'. Scene Converter: Parsing textures. Scene Converter: Parsing materials. Scene Converter: Parsing animations. Scene Converter: Parsing meshes. Scene Converter: Parsing nodes. Scene Converter: Scene info: Scene Converter: Number of textures: 0 Scene Converter: Number of materials: 1 Scene Converter: Material 0000: uc=0 name="DefaultMaterial" ambientColor=ff000000 diffuseColor=ff999999 specularColor=ff000000 emissiveColor=ff000000 shininess=0.000000 numTextures=0 Scene Converter: Number of animations: 1 Scene Converter: Animation 0000: uc=0 name="Anim0" Scene Converter: Number of bones: 0 Scene Converter: Number of meshes: 1 Scene Converter: Mesh 0000: uc=0 name="Mesh0" materialName="DefaultMaterial" numFaces=8087 numVertices=7125 numBones=0 nm=0 nc=0 Scene Converter: Number of nodes: 3 Scene Converter: Node 0000: uc=0 name="" numChildren=1 numMeshes=0 numBones=0 Scene Converter: Child 0000: index=0001 name="Darius.obj" Scene Converter: Node 0001: uc=0 name="Darius.obj" numChildren=1 numMeshes=0 numBones=0 Scene Converter: Child 0000: index=0002 name="gx20default043" Scene Converter: Node 0002: uc=0 name="gx20default043" numChildren=0 numMeshes=1 numBones=0 Scene Converter: Mesh 0000: index=0000 name="Mesh0" Scene Converter: Skeleton Graph: Scene Converter: Model Graph: Scene Converter: Node 0000: name="" numMeshes=0 Scene Converter: Node 0000: name="" numMeshes=0 Scene Converter: Node 0001: name="Darius.obj" numMeshes=0 Scene Converter: Node 0002: name="gx20default043" numMeshes=1 Scene Converter: Mesh 0000: index=0000 name="Mesh0"
Es gibt zwei Probleme mit diesem Modell:
Erstens wird die Textur in einem TGA-Format geliefert, ein Bildformat, das die Murl Engine nicht unterstützt. Wir konvertieren daher das Bild mit einem geeigneten Programm in das PNG-Format.
Zweitens zeigt die Analyse, dass im Material fälschlicherweise keine Textur definiert ist. Wenn wir das Modell konvertieren, wird das Modell auch ohne Textur gerendert:
Wir müssen also die Textur händisch hinzufügen.
<ImageLocation pathName="./darius"/> <Material findIdMatchingPattern="*"> <Texture createWithFileName="Darius_color.png"/> </Material>
Mit dieser Änderung wird das 3D-Modell richtig angezeigt (Bildausschnitt links).
Wir können das Ergebnis noch verbessern, indem wir mit ambientColor
die Farben aufhellen. Zusätzlich reduzieren wir mit den Attributen sizeX
und sizeY
die Texturgröße von 2048x2048 Pixel auf 1024x1024 Pixel (Bildausschnitt rechts).
<ImageLocation pathName="./darius"/> <Material findIdMatchingPattern="*" ambientColor="ffffffff"> <Texture createWithFileName="Darius_color.png" sizeX="1024" sizeY="1024"/> </Material>
Pony
Dieses Modell nenn sich Pony, wurde von Slava Zhuravlev erstellt und steht unter der Creative Commons Attribution Lizenz.
- Zu beachten
- Der Autor hat zwischenzeitlich die Downloadoption auf Sketchfab entfernt und das Modell geändert. Ein ähnliches Modell, Pony Cartoon, kann aber noch downgeloaded und frei verwendet werden. Thx @SaphireSouldier für den Hinweis.
Im Rar-Archiv befinden sich das 3D-Modell im OBJ-Format sowie verschiedene Texturen.
Beim Konvertieren erhalten wir Fehlermeldungen, da einzelne Texturen entweder nicht gefunden werden oder in einem nicht unterstützten Dateiformat vorliegen:
Scene Converter: Creating material texture for type 5 from file 'normalmap_dDo_n_2.png -bm 1'. Scene Converter: Error Failed to load texture file 'normalmap_dDo_n_2.png -bm 1' for texture 'normalmap_dDo_n_2.png -bm 1' Scene Converter: Creating material texture for type 5 from file 'interior_normalmap_2.PNG -bm 1'. Scene Converter: Error Failed to load texture file 'interior_normalmap_2.PNG -bm 1' for texture 'interior_normalmap_2.PNG -bm 1' Scene Converter: Creating material texture for type 1 from file 'Road_diffuse.psd'. Scene Converter: Error Failed to decode texture 'Road_diffuse.psd' Scene Converter: Error Failed to load texture file 'Road_diffuse.psd' for texture 'Road_diffuse.psd'
Wir müssen also die PSD-Datei in ein PNG-Dateiformat umwandeln und die Namen der Texturen korrigieren:
<ImageLocation pathName="./pony"/> <Texture findFileNameMatchingPattern="normalmap_dDo_n_2.png -bm 1" replaceWithFileName="normalmap_dDo_n_2.png"/>; <Texture findFileNameMatchingPattern="interior_normalmap_2.PNG -bm 1" replaceWithFileName="interior_normalmap_2.jpg"/>; <Texture findFileNameMatchingPattern="Road_diffuse.psd" replaceWithFileName="Road_diffuse.png"/>;
Mit diesen Änderungen lässt sich das Modell konvertieren. Allerdings ist des Ergebnis viel zu dunkel (Bildausschnitt links). Daher korrigieren wir noch die Farbparameter der Materialien:
<Material findIdMatchingPattern="*" ambientColor="1f,1f,1f,1f" diffuseColor="1f,1f,1f,1f" specularColor="0.4f,0.4f,0.4f,1f" shininess="10"/>
Das Ergebnis ist besser. Allerdings wird der Boden ohne Transparenz gezeichnet (Bildausschnitt rechts). Wir korrigieren das Material für den Boden und erhalten ein zufriedenstellendes Ergebnis:
<Material findIdMatchingPattern="lambert3SG" blendMode="ALPHA"/>
Ein letztes Problem bleibt bestehen: Die Fenster des Autos sollten normalerweise transparent sein, werden aber ohne Transparenz angezeigt. Der Grund dafür ist, dass laut Scene Converter Analyse die Transparenz (OPACITY_VALUE) im Alphakanal der diffusen Textur gespeichert ist.
Scene Converter: Texture 0000: index=0000 name="normalmap_dDo_d.jpg" semantic="DIFFUSE_RED/DIFFUSE_GREEN/DIFFUSE_BLUE/OPACITY_VALUE"
Die mitgelieferte Textur wurde aber im JPG-Format gespeichert, also einem Dateiformat, welches einen Alphakanal nicht unterstützt. Die Transparenzinformation ist dadurch verloren gegangen. Dieses Problem könnten wir nur beheben, indem wir die Textur im PNG-Format abspeichern und in einem Grafikprogramm die Fenster im Alphakanal nachzeichnen.
- Zu beachten
- Tipp! Aus Geschwindigkeitsgründen ist es immer besser transparente Flächen als eigene Mesh mit eigenem Material zu modellieren, da anderenfalls auch die restliche opaque Geometrie mit teurem Alpha-Blending gezeichnet werden muss.
Nützliche Konfigurationsparameter
Hier nochmal die häufig bei der Konvertierung von 3D-Modellen benötigten Konfigurationsparameter.
Weitere Ressourcen-Pakete für den Scene-Viewer angeben:
<InputPackage fileName="../packages/materials.murlres"/>
Suchpfad für Textur-Bilder:
<ImageLocation pathName="./myPath"/>
Textur ersetzten:
<!-- TGA-Dateien können nicht importiert werden, stattdessen konvertiertes PNG verwenden --> <Texture findFileNameMatchingPattern="myTextureImage.tga" replaceWithFileName="myTextureImage.png"/>
Ersetzen und Größe der Textur anpassen (Zusätzlich zu sizeX
und sizeY
stehen noch folgende Parameter zur Verfügung: flipX
, flipY
, rotation
mit IEnums::Orientation
Werten):
<Texture findFileNameMatchingPattern="color.jpg" replaceWithFileName="grey.jpg" sizeX="1024" sizeY="1024"/>
Material ersetzen:
<Material findIdMatchingPattern="Matte__FF000000_" replaceWithGraphNodeId="/MaterialMainMatte"/>
Material durch Material-Slot ersetzen:
<Material findIdMatchingPattern="green" replaceWithSlot="8"/>
oder
<Material findIdMatchingPattern="*" replaceWithMaterialSlot="3" replaceWithTextureSlot="0" replaceWithParametersSlot="5"> <Texture createWithSlot="0"/> </Material>
Material Blend-Mode ändern:
<Material findIdMatchingPattern="*" blendMode="NONE"/>
Material ändern, Farben ändern und Textur hinzufügen:
<!-- Farbanpassung der Materialien --> <Material findIdMatchingPattern="*" replaceWithGraphNodeId="/MaterialMain" ambientColor="1f,1f,1f,1f" diffuseColor="1f,1f,1f,1f" specularColor="1f,1f,1f,1f" shininess="50"> <!-- Szene definiert keine Textur für Materialien, händisch hinzufügen --> <Texture createWithFileName="raf22031.jpg" flipY="yes"/> </Material>
Generate Mesh-Colliders:
<GraphInstance graphId="GraphInstance" filePrefix="graph_instance" generateMeshes="yes" generateColliders="yes"/>