OpenSceneGraph: Texturgrundlagen

Bild

Einführung


Wir haben bereits ein Beispiel untersucht, in dem ein Quadrat in allen Farben des Regenbogens gemalt wurde. Dennoch gibt es eine andere Technologie, nämlich die Anwendung auf die dreidimensionale Geometrie der sogenannten Texturkarte oder einfach nur Textur - ein zweidimensionales Rasterbild. In diesem Fall wirkt sich der Effekt nicht auf die Scheitelpunkte der Geometrie aus, sondern die Daten aller Pixel, die während der Rasterung der Szene erhalten werden, ändern sich. Diese Technik kann den Realismus und die Details des endgültigen Bildes erheblich verbessern.

OSG unterstützt verschiedene Texturattribute und Texturierungsmodi. Bevor wir jedoch über Texturen sprechen, lassen Sie uns darüber sprechen, wie OSG mit Bitmap-Bildern umgeht. Für die Arbeit mit Rasterbildern wird eine spezielle Klasse bereitgestellt - osg :: Image, in der Bilddaten gespeichert werden, die letztendlich zur Texturierung des Objekts bestimmt sind.

1. Präsentation von Rasterbilddaten. Osg class :: Image


Der beste Weg, ein Image von der Festplatte zu laden, ist die Verwendung des Aufrufs osgDB :: readImageFile (). Es ist dem Aufruf von osg :: readNodeFile () sehr ähnlich, der uns bereits gelangweilt hat. Wenn wir eine Bitmap mit dem Namen picture.bmp haben, sieht das Laden folgendermaßen aus

osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); 

Wenn das Bild korrekt geladen ist, ist der Zeiger gültig, andernfalls gibt die Funktion NULL zurück. Nach dem Herunterladen können wir Bildinformationen mit den folgenden öffentlichen Methoden erhalten

  1. t (), s () und r () - geben die Breite, Höhe und Tiefe des Bildes zurück.
  2. data () - gibt einen Zeiger vom Typ unsigned char * auf die "rohen" Bilddaten zurück. Über diesen Zeiger kann der Entwickler direkt auf die Bilddaten reagieren. Mit den Methoden getPixalFormat () und getDataType () können Sie sich ein Bild vom Bilddatenformat machen. Die von ihnen zurückgegebenen Werte entsprechen den Parametern des Formats und des Typs der OpenGL-Funktionen glTexImage * (). Wenn das Bild beispielsweise das Pixelformat GL_RGB hat und der Typ GL_UNSIGNED_BYTE ist, werden drei unabhängige Elemente (vorzeichenlose Bytes) verwendet, um die RGB-Farbkomponente darzustellen



Sie können ein neues Bildobjekt erstellen und Speicher dafür zuweisen.

 osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage(s, t, r, GL_RGB, GL_UNSIGNED_BYTE); unsigned char *ptr = image->data(); //         

Hier sind s, t, r Bildgrößen; GL_RGB legt das Pixelformat fest und GL_UNSIGNED_BYTE legt den Datentyp fest, um eine einzelne Farbkomponente zu beschreiben. Ein interner Datenpuffer der erforderlichen Größe wird im Speicher zugewiesen und automatisch zerstört, wenn keine einzige Verknüpfung zu diesem Bild besteht.

Das OSG-Plug-In-System unterstützt das Herunterladen fast aller gängigen Bildformate: * .jpg, * .bmp, * .png, * .tif usw. Diese Liste kann leicht erweitert werden, indem Sie Ihr eigenes Plugin schreiben. Dies ist jedoch ein Thema für eine andere Diskussion.

2. Die Grundlagen der Texturierung


Um eine Textur auf ein dreidimensionales Modell anzuwenden, müssen Sie eine Reihe von Schritten ausführen:

  1. Definieren Sie die Texturkoordinaten der Scheitelpunkte für das geometrische Objekt (in der Umgebung von 3D-Designern wird dies als UV-Scan bezeichnet).
  2. Erstellen Sie ein Texturattributobjekt für 1D-, 2D-, 3D- oder kubische Textur.
  3. Legen Sie ein oder mehrere Bilder für ein Texturattribut fest.
  4. Fügen Sie der Gruppe von Zuständen, die auf das zu zeichnende Objekt angewendet werden, ein Texturattribut und einen Modus hinzu.

OSG definiert die Klasse osg :: Texture, die alle Arten von Texturen kapselt. Von diesen werden die Unterklassen osg :: Texture1D, osg :: Texture2D, osg :: Texture3D und osg :: TextureCubeMap geerbt, die verschiedene in OpenGL verwendete Texturierungstechniken darstellen.

Die gebräuchlichste osg :: Texture-Klassenmethode ist setImage (), mit der beispielsweise das in der Textur verwendete Bild festgelegt wird

 osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setImage(image.get()); 

Sie können das Bildobjekt auch direkt an den Konstruktor der Texturklasse übergeben

 osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D(image.get()); 

Das Bild kann durch Aufrufen der Methode getImage () aus dem Texturobjekt abgerufen werden.

Ein weiterer wichtiger Punkt ist das Festlegen der Texturkoordinaten für jeden Scheitelpunkt im osg :: Geometry-Objekt. Die Übertragung dieser Koordinaten erfolgt über das Array osg :: Vec2Array und osg :: Vec3Array durch Aufrufen der Methode setTexCoordArray ().

Nach dem Festlegen der Texturkoordinaten müssen wir die Texturschlitznummer (Einheit) festlegen, da OSG das Überlagern mehrerer Texturen mit derselben Geometrie unterstützt. Bei Verwendung einer einzelnen Textur ist die Einheitennummer immer 0. Der folgende Code veranschaulicht beispielsweise das Festlegen der Texturkoordinaten für die Einheit 0 der Geometrie

 osf::ref_ptr<osg::Vec2Array> texcoord = new osg::Vec2Array; texcoord->push_back( osg::Vec2(...) ); ... geom->setTexCoordArray(0, texcoord.get()); 

Danach können wir das Texturattribut zu der Gruppe von Zuständen hinzufügen, den entsprechenden Texturierungsmodus automatisch aktivieren (in unserem Beispiel GL_TEXTURE_2D) und das Attribut auf die Geometrie oder den Knoten anwenden, der diese Geometrie enthält

 geom->getOrCreateStateSet()->setTextureAttributeAndModes(texture.get()); 

Bitte beachten Sie, dass OpenGL die Bilddaten im Grafikspeicher der Grafikkarte verwaltet, sich jedoch das osg :: Image-Objekt zusammen mit denselben Daten im Systemspeicher befindet. Infolgedessen werden wir feststellen, dass wir zwei Kopien derselben Daten gespeichert haben, die den Prozessspeicher belegen. Wenn dieses Image nicht von mehreren Texturattributen gemeinsam genutzt wird, kann es sofort nach der Übertragung durch OpenGL in den Videoadapterspeicher aus dem Systemspeicher gelöscht werden. Die Klasse osg :: Texture bietet die entsprechende Methode, um diese Funktion zu aktivieren.

 texture->setUnRefImageDataAfterApply( true ); 

3. Laden Sie eine 2D-Textur und wenden Sie sie an


Die am häufigsten verwendete Technik ist die 2D-Texturierung - Überlagern eines zweidimensionalen Bildes (oder von zweidimensionalen Bildern) am Rand einer dreidimensionalen Oberfläche. Betrachten Sie das einfachste Beispiel für das Anwenden einer einzelnen Textur auf ein viereckiges Polygon

Texturbeispiel
main.h

 #ifndef MAIN_H #define MAIN_H #include <osg/Texture2D> #include <osg/Geometry> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif 

main.cpp

 #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(-0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, 0.5f) ); vertices->push_back( osg::Vec3(-0.5f, 0.0f, 0.5f) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) ); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; osg::ref_ptr<osg::Image> image = osgDB::readImageFile("../data/Images/lz.rgb"); texture->setImage(image.get()); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); root->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Erstellen Sie ein Array von Eckpunkten und Normalen zum Gesicht

 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(-0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, 0.5f) ); vertices->push_back( osg::Vec3(-0.5f, 0.0f, 0.5f) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); 

Erstellen Sie ein Array von Texturkoordinaten

 osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); 

Der Punkt ist, dass jeder Scheitelpunkt des dreidimensionalen Modells einem Punkt auf der zweidimensionalen Textur entspricht und die Koordinaten der Punkte auf der Textur relativ sind - sie werden auf die tatsächliche Breite und Höhe des Bildes normalisiert. Wir wollen das gesamte geladene Bild auf ein Quadrat strecken, wobei die Ecken des Quadrats den Texturpunkten (0, 0), (0, 1), (1, 1) und (1, 0) entsprechen. Die Reihenfolge der Scheitelpunkte im Scheitelpunktarray muss mit der Reihenfolge der Texturscheitelpunkte übereinstimmen.

Erstellen Sie als Nächstes ein Quadrat und weisen Sie der Geometrie ein Array von Eckpunkten und ein Array von Normalen zu

 osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) ); 

Erstellen Sie ein Texturobjekt und laden Sie das dafür verwendete Bild.

 osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; osg::ref_ptr<osg::Image> image = osgDB::readImageFile("../data/Images/lz.rgb"); texture->setImage(image.get()); 

Erstellen Sie den Wurzelknoten der Szene und fügen Sie die dort erstellte Geometrie ein

 osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); 

und schließlich das Texturattribut auf den Knoten anwenden, in dem die Geometrie platziert ist

 root->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); 



Die Klasse osg :: Texture2D bestimmt mithilfe der OpenGL-Funktion gluScaleImage (), ob Texturbildgrößen ein Vielfaches der Zweierpotenz sind (z. B. 64 x 64 oder 256 x 512), und skaliert automatisch Bilder, die für die Größe ungeeignet sind. Es gibt eine setResizeNonPowerOfTwoHint () -Methode, die bestimmt, ob die Bildgröße geändert werden soll oder nicht. Einige Grafikkarten erfordern ein Vielfaches der Größe eines Bildes mit der Potenz von zwei, während die Klasse osg :: Texture2D das Arbeiten mit beliebigen Texturgrößen unterstützt.

Etwas über das Mischen von Texturen


Wie bereits erwähnt, werden die Texturkoordinaten von 0 auf 1 normalisiert. Punkt (0, 0) entspricht der oberen linken Ecke des Bildes und Punkt (1, 1) entspricht der unteren rechten Ecke. Was passiert, wenn Sie Texturkoordinaten größer als eins festlegen?

Standardmäßig wird in OpenGL wie in OSG die Textur in Richtung der Achse wiederholt, der Wert der Texturkoordinate überschreitet eins. Diese Technik wird oft verwendet, um beispielsweise ein Modell einer langen Mauer zu erstellen. Ich verwende eine kleine Textur, die ihre Überlagerung sowohl in der Breite als auch in der Höhe viele Male wiederholt.

Dieses Verhalten kann über die setWrap () -Methode der Klasse osg :: Texture gesteuert werden. Als ersten Parameter verwendet die Methode den Bezeichner der Achse, auf die der Mischmodus angewendet werden soll, beispielsweise als zweiten Parameter

 //     s texture->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT ); //     r texture->setWrap( osg::Texture::WRAP_R, osg::Texture::REPEAT ); 

Dieser Code weist die Engine ausdrücklich an, die Textur entlang der s- und r-Achse zu wiederholen, wenn die Texturkoordinatenwerte 1 überschreiten. Eine vollständige Liste der Texturabbildungsmodi:

  1. WIEDERHOLEN - Wiederholen Sie die Textur.
  2. SPIEGEL - Wiederholen Sie die Textur und spiegeln Sie sie.
  3. CLAMP_TO_EDGE - Koordinaten, die über 0 bis 1 hinausgehen, werden an der entsprechenden Kante der Textur gefangen.
  4. CLAMP_TO_BORDER - Koordinaten, die über 0 bis 1 hinausgehen, geben die vom Benutzer festgelegte Rahmenfarbe an.

4. Rendern zur Textur


Die Textur-Rendering-Technik ermöglicht es dem Entwickler, eine Textur basierend auf einer dreidimensionalen Unterszene oder einem dreidimensionalen Modell zu erstellen und auf die Oberfläche in der Hauptszene anzuwenden. Eine ähnliche Technologie wird oft als Texturbacken bezeichnet.

Um eine Textur dynamisch zu backen, müssen Sie drei Schritte ausführen:

  1. Erstellen Sie ein Texturobjekt, um es zu rendern.
  2. Rendern Sie die Szene in eine Textur.
  3. Verwenden Sie die resultierende Textur wie vorgesehen.

Wir müssen ein leeres Texturobjekt erstellen. Mit OSG können Sie eine leere Textur einer bestimmten Größe erstellen. Mit der Methode setTextureSize () können Sie die Breite und Höhe der Textur sowie die Tiefe als zusätzlichen Parameter festlegen (für 3D-Texturen).

Um eine Textur zu rendern, sollten Sie sie an das Kameraobjekt anhängen, indem Sie die Methode attach () aufrufen, die ein Texturobjekt als Argument verwendet. Darüber hinaus akzeptiert diese Methode ein Argument, das angibt, welcher Teil des Bildpuffers in diese Textur gerendert werden soll. Um beispielsweise einen Farbpuffer in eine Textur zu übertragen, sollten Sie den folgenden Code ausführen

 camera->attach( osg::Camera::COLOR_BUFFER, texture.get() ); 

Weitere Teile des für das Rendern verfügbaren Bildpuffers sind der Tiefenpuffer DEPTH_BUFFER, der Schablonenpuffer STENCIL_BUFFER und zusätzliche Farbpuffer von COLOR_BUFFER0 bis COLOR_BUFFER15. Das Vorhandensein zusätzlicher Farbpuffer und deren Anzahl wird durch das Modell der Grafikkarte bestimmt.

Darüber hinaus müssen Sie für eine Kamera, die in eine Textur gerendert wird, die Parameter der Projektionsmatrix und des Ansichtsfensters festlegen, deren Größe der Größe der Textur entspricht. Die Textur wird aktualisiert, wenn jeder Frame gezeichnet wird. Beachten Sie, dass die Hauptkamera nicht zum Rendern in eine Textur verwendet werden sollte, da sie das Rendern der Hauptszene ermöglicht und Sie nur einen schwarzen Bildschirm erhalten. Diese Anforderung wird möglicherweise nicht nur erfüllt, wenn Sie ein Rendern außerhalb des Bildschirms durchführen.

5. Ein Beispiel für die Implementierung des Renderns in Textur


Um die Rendering-Technik in einer Textur zu demonstrieren, implementieren wir die folgende Aufgabe: Erstellen Sie ein Quadrat, zeichnen Sie eine quadratische Textur darauf und rendern Sie eine animierte Szene in die Textur, natürlich mit der Cessna, die wir lieben. Das Programm, das das Beispiel implementiert, ist ziemlich umfangreich. Ich werde jedoch trotzdem den vollständigen Quellcode angeben.

Texrender Beispiel
main.h

 #ifndef MAIN_H #define MAIN_H #include <osg/Camera> #include <osg/Texture2D> #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgGA/TrackballManipulator> #include <osgViewer/Viewer> #endif 

main.cpp

 #include "main.h" //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ osg::Geometry *createQuad(const osg::Vec3 &pos, float w, float h) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, -h / 2) ); vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, -h / 2) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); return quad.release(); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::rotate(0.0, osg::Vec3(0.0f, 0.0f, 1.0f))); transform1->addChild(sub_model.get()); osg::ref_ptr<osg::Geode> model = new osg::Geode; model->addChild(createQuad(osg::Vec3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f)); int tex_widht = 1024; int tex_height = 1024; osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setTextureSize(tex_widht, tex_height); texture->setInternalFormat(GL_RGBA); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); model->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); osg::ref_ptr<osg::Camera> camera = new osg::Camera; camera->setViewport(0, 0, tex_widht, tex_height); camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->attach(osg::Camera::COLOR_BUFFER, texture.get()); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->addChild(transform1.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get()); root->addChild(camera.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setUpViewOnSingleScreen(0); camera->setProjectionMatrixAsPerspective(30.0, static_cast<double>(tex_widht) / static_cast<double>(tex_height), 0.1, 1000.0); float dist = 100.0f; float alpha = 10.0f * 3.14f / 180.0f; osg::Vec3 eye(0.0f, -dist * cosf(alpha), dist * sinf(alpha)); osg::Vec3 center(0.0f, 0.0f, 0.0f); osg::Vec3 up(0.0f, 0.0f, -1.0f); camera->setViewMatrixAsLookAt(eye, center, up); float phi = 0.0f; float delta = -0.01f; while (!viewer.done()) { transform1->setMatrix(osg::Matrix::rotate(static_cast<double>(phi), osg::Vec3(0.0f, 0.0f, 1.0f))); viewer.frame(); phi += delta; } return 0; } 


Um ein Quadrat zu erstellen, schreiben Sie eine separate freie Funktion

 osg::Geometry *createQuad(const osg::Vec3 &pos, float w, float h) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, -h / 2) ); vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, -h / 2) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); return quad.release(); } 

Die Funktion akzeptiert die Position der Mitte des Quadrats und seine geometrischen Abmessungen als Eingabe. Als nächstes wird ein Array von Eckpunkten, ein Array von Normalen und Texturkoordinaten erstellt, wonach die erstellte Geometrie von der Funktion zurückgegeben wird.

Laden Sie im Hauptteil des Hauptprogramms das Modell der Cessna

 osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("../data/cessna.osg"); 

Um dieses Modell zu animieren, erstellen und initialisieren Sie die Rotationstransformation um die Z-Achse

 osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::rotate(0.0, osg::Vec3(0.0f, 0.0f, 1.0f))); transform1->addChild(sub_model.get()); 

Erstellen Sie nun ein Modell für die Hauptszene - ein Quadrat, auf dem wir rendern werden

 osg::ref_ptr<osg::Geode> model = new osg::Geode; model->addChild(createQuad(osg::Vec3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f)); 

Erstellen Sie eine leere Textur für ein 1024 x 1024 Pixel großes Quadrat mit einem RGBA-Pixelformat (32-Bit-Dreikomponentenfarbe mit Alphakanal).

 int tex_widht = 1024; int tex_height = 1024; osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setTextureSize(tex_widht, tex_height); texture->setInternalFormat(GL_RGBA); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); 

Wenden Sie diese Textur auf das quadratische Modell an.

 model->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); 

Erstellen Sie dann eine Kamera, die die Textur backt

 osg::ref_ptr<osg::Camera> camera = new osg::Camera; camera->setViewport(0, 0, tex_widht, tex_height); camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

Das Kamera-Ansichtsfenster hat dieselbe Größe wie die Textur. Vergessen Sie außerdem nicht, die Hintergrundfarbe beim Reinigen des Bildschirms und der Reinigungsmaske festzulegen, um sowohl den Farbpuffer als auch den Tiefenpuffer zu löschen. Konfigurieren Sie als Nächstes die Kamera so, dass sie in Textur gerendert wird

 camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->attach(osg::Camera::COLOR_BUFFER, texture.get()); 

Die Renderreihenfolge von PRE_RENDER gibt an, dass diese Kamera vor dem Rendern in der Hauptszene gerendert wird. Geben Sie den FBO als Rendering-Ziel an und hängen Sie unsere Textur an die Kamera an. Jetzt richten wir die Kamera so ein, dass sie in einem absoluten Koordinatensystem arbeitet, und als Szene setzen wir unseren Teilbaum, den wir in eine Textur rendern möchten: eine Rotationstransformation mit einem daran angehängten Cessna-Modell

 camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->addChild(transform1.get()); 

Erstellen Sie einen Stammgruppenknoten, indem Sie das Hauptmodell (Quadrat) und eine Kamera-Verarbeitungstextur hinzufügen

 osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get()); root->addChild(camera.get()); 

Erstellen Sie einen Viewer und passen Sie ihn an

 osgViewer::Viewer viewer; viewer.setSceneData(root.get()); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setUpViewOnSingleScreen(0); 

Richten Sie die Projektionsmatrix für die Kamera ein - eine perspektivische Projektion durch die Parameter der Schnittpyramide

 camera->setProjectionMatrixAsPerspective(30.0, static_cast<double>(tex_widht) / static_cast<double>(tex_height), 0.1, 1000.0); 

Wir haben eine Ansichtsmatrix erstellt, die die Position der Kamera im Raum in Bezug auf den Ursprung der Sub-Cessna festlegt

 float dist = 100.0f; float alpha = 10.0f * 3.14f / 180.0f; osg::Vec3 eye(0.0f, -dist * cosf(alpha), dist * sinf(alpha)); osg::Vec3 center(0.0f, 0.0f, 0.0f); osg::Vec3 up(0.0f, 0.0f, -1.0f); camera->setViewMatrixAsLookAt(eye, center, up); 

Zum Schluss animieren und zeigen Sie die Szene an, indem Sie den Drehwinkel des Flugzeugs um die Z-Achse in jedem Bild ändern

 float phi = 0.0f; float delta = -0.01f; while (!viewer.done()) { transform1->setMatrix(osg::Matrix::rotate(static_cast<double>(phi), osg::Vec3(0.0f, 0.0f, 1.0f))); viewer.frame(); phi += delta; } 

Als Ergebnis erhalten wir ein ziemlich interessantes Bild



In diesem Beispiel haben wir einige Szenenanimationen implementiert. Beachten Sie jedoch, dass das Erweitern der run () - Schleife und das Ändern der Renderparameter vor oder nach dem Rendern des Frames eine unsichere Aktivität ist, um den Zugriff auf Daten aus verschiedenen Streams zu organisieren. Da OSG Multithread-Rendering verwendet, gibt es auch regelmäßige Mechanismen zum Einbetten eigener Aktionen in den Rendering-Prozess, die einen thread-sicheren Zugriff auf Daten ermöglichen.

6. Speichern Sie das Rendering-Ergebnis in einer Datei


OSG unterstützt die Möglichkeit, ein osg :: Image-Objekt an die Kamera anzuhängen und den Inhalt des Bildpuffers im Bilddatenpuffer zu speichern. Danach können diese Daten mit der Funktion osg :: writeImageFile () auf der Festplatte gespeichert werden

 osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage( width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE ); camera->attach( osg::Camera::COLOR_BUFFER, image.get() ); ... osgDB::writeImageFile( *image, "saved_image.bmp" ); 

Fazit


Vielleicht scheint das im Artikel vorgestellte Material trivial zu sein. Es werden jedoch die Grundlagen der Arbeit mit Texturen in OpenSceneGraph beschrieben, auf denen komplexere Techniken für die Arbeit mit dieser Engine basieren, über die wir in Zukunft definitiv sprechen werden.

Fortsetzung folgt...

Source: https://habr.com/ru/post/de437624/


All Articles