OpenSceneGraph: أساسيات الملمس

الصورة

مقدمة


لقد درسنا بالفعل مثالًا حيث رسمت مربعًا بألوان قوس قزح. ومع ذلك ، هناك تقنية أخرى ، وهي التطبيق على الهندسة ثلاثية الأبعاد لخريطة الملمس أو الملمس فقط - صورة نقطية ثنائية الأبعاد. في هذه الحالة ، لا يكون التأثير على رؤوس الأشكال الهندسية ، ولكن يتم تغيير بيانات جميع وحدات البكسل التي تم الحصول عليها عندما يتم تنقيط المشهد. هذه التقنية يمكن أن تزيد بشكل كبير من الواقعية وتفاصيل الصورة النهائية.

يدعم OSG العديد من سمات الملمس وأنماط التركيب. ولكن ، قبل التحدث عن القوام ، فلنتحدث عن كيفية تعامل OSG مع الصور النقطية. للعمل مع الصور النقطية ، يتم توفير فئة خاصة - osg :: Image ، التي تخزن بيانات الصورة بداخلها ، في نهاية المطاف ، لتركيب الكائن.

1. عرض بيانات الصورة النقطية. Osg الطبقة :: الصورة


أفضل طريقة لتحميل صورة من القرص هي استخدام استدعاء osgDB :: readImageFile (). إنها تشبه إلى حد بعيد استدعاء osg :: readNodeFile () ، الذي أصابنا بالملل بالفعل. إذا كان لدينا صورة نقطية باسم picture.bmp ، فسيبدو تحميلها هكذا

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

إذا تم تحميل الصورة بشكل صحيح ، فسيكون المؤشر صالحًا ، وإلا ستعود الدالة NULL. بعد التنزيل ، يمكننا الحصول على معلومات الصورة باستخدام الطرق العامة التالية

  1. t () و s () و r () - إرجاع عرض الصورة وارتفاعها وعمقها.
  2. تقوم البيانات () - بإرجاع مؤشر من النوع char غير موقعة * إلى بيانات الصورة "الأولية". من خلال هذا المؤشر ، يمكن للمطور التصرف مباشرة في بيانات الصورة. يمكنك الحصول على فكرة عن تنسيق بيانات الصورة باستخدام طريقتي getPixalFormat () و getDataType (). القيم التي يتم إرجاعها بواسطتها مكافئة لمعلمات التنسيق ونوع وظائف OpenGL glTexImage * (). على سبيل المثال ، إذا كانت الصورة بتنسيق GL_RGB بكسل وكان النوع GL_UNSIGNED_BYTE ، فسيتم استخدام ثلاثة عناصر مستقلة (بايت غير موقعة) لتمثيل مكون ألوان RGB



يمكنك إنشاء كائن صورة جديد وتخصيص ذاكرة له.

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

هنا s ، t ، r هي أحجام الصور ؛ يعيّن GL_RGB تنسيق البكسل ، ويقوم GL_UNSIGNED_BYTE بتعيين نوع البيانات لوصف مكون لون واحد. يتم تخصيص مخزن مؤقت للبيانات الداخلية بالحجم المطلوب في الذاكرة ويتم إتلافه تلقائيًا إذا لم يكن هناك رابط واحد لهذه الصورة.

يدعم نظام المكونات OSG تنزيل جميع تنسيقات الصور الشائعة تقريبًا: * .jpg ، * .bmp ، * .png ، * .tif وما إلى ذلك. من السهل توسيع هذه القائمة عن طريق كتابة المكوّن الإضافي الخاص بك ، ولكن هذا موضوع لمناقشة أخرى.

2. أساسيات التركيب


لتطبيق مادة على نموذج ثلاثي الأبعاد ، تحتاج إلى تنفيذ عدد من الخطوات:

  1. حدد إحداثيات نسيج الرؤوس للعنصر الهندسي (في بيئة المصممين ثلاثي الأبعاد ، وهذا ما يسمى فحص الأشعة فوق البنفسجية).
  2. إنشاء كائن سمة نسيج ل 1 D ، 2D ، 3D ، أو نسيج مكعب.
  3. اضبط صورة واحدة أو أكثر لسمة نسيج.
  4. قم بإرفاق سمة نسيج ووضع في مجموعة الحالات المطبقة على الكائن الذي يتم رسمه.

يعرّف OSG فئة osg :: Texture ، التي تضم جميع أنواع القوام. الفئات الفرعية osg :: Texture1D ، osg :: Texture2D ، osg :: Texture3D و osg :: TextureCubeMap موروثة منه ، والتي تمثل مختلف تقنيات التركيب المعتمدة في OpenGL.

أكثر طريقة لفئة osg :: Texture شيوعًا هي setImage () ، والتي تحدد الصورة المستخدمة في النسيج ، على سبيل المثال

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

أو ، يمكنك تمرير كائن الصورة مباشرة إلى مُنشئ فئة النسيج

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

يمكن استرداد الصورة من كائن النسيج عن طريق استدعاء الأسلوب getImage ().

النقطة المهمة الأخرى هي إعداد إحداثيات المادة لكل قمة في كائن osg :: Geometry. يحدث نقل هذه الإحداثيات من خلال صفيف osg :: Vec2Array و osg :: Vec3Array عن طريق استدعاء الأسلوب setTexCoordArray ().

بعد ضبط إحداثيات النسيج ، نحتاج إلى تعيين رقم فتحة النسيج (الوحدة) ، حيث أن OSG يدعم تركيب مواد متعددة على نفس الشكل الهندسي. عند استخدام نسيج واحد ، يكون رقم الوحدة دائمًا 0. على سبيل المثال ، توضح التعليمة البرمجية التالية إعداد إحداثيات المادة للوحدة 0 من الهندسة

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

بعد ذلك ، يمكننا إضافة السمة texture إلى مجموعة الحالات ، وتشغيل وضع التركيب المقابل تلقائيًا (في مثالنا GL_TEXTURE_2D) وتطبيق السمة على الشكل الهندسي أو العقدة التي تحتوي على هذه الهندسة

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

يرجى ملاحظة أن برنامج OpenGL يدير بيانات الصورة في ذاكرة الرسومات الخاصة ببطاقة الفيديو ، ولكن يوجد كائن osg :: Image مع نفس البيانات في ذاكرة النظام. نتيجة لذلك ، سنواجه حقيقة أننا قمنا بتخزين نسختين من نفس البيانات ، ونشغل ذاكرة العملية. إذا لم تتم مشاركة هذه الصورة بواسطة العديد من سمات النسيج ، فيمكن حذفها من ذاكرة النظام فور نقلها OpenGL إلى ذاكرة محول الفيديو. توفر الفئة osg :: Texture الطريقة المناسبة لتمكين هذه الوظيفة.

 texture->setUnRefImageDataAfterApply( true ); 

3. تحميل وتطبيق الملمس 2D


الأسلوب الأكثر شيوعًا هو التركيب ثنائي الأبعاد - حيث يتراكب مع صورة ثنائية الأبعاد (أو صور) على حافة سطح ثلاثي الأبعاد. النظر في أبسط مثال على تطبيق نسيج واحد على مضلع رباعي الزوايا

مثال الملمس
الرئيسية

 #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(); } 


إنشاء مجموعة من القمم و normals على الوجه

 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) ); 

النقطة المهمة هي أن كل رأس من النموذج ثلاثي الأبعاد يتوافق مع نقطة على النسيج ثنائي الأبعاد ، وأن إحداثيات النقاط على النسيج نسبية - يتم تطبيعها مع العرض والارتفاع الفعلي للصورة. نريد تمديد الصورة المحملة بالكامل على مربع ، على التوالي ، سوف تتوافق زوايا المربع مع نقاط النسيج (0 ، 0) ، (0 ، 1) ، (1 ، 1) و (1 ، 0). يجب أن يتطابق ترتيب القمم في صفيف الرأس مع ترتيب قوام النسيج.

بعد ذلك ، قم بإنشاء مربع ، مع تحديد الشكل الهندسي بمجموعة من الرؤوس ومجموعة من الحالات الطبيعية

 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()); 

وأخيرًا قم بتطبيق السمة texture على العقدة التي وضعت فيها الهندسة

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



تحدد فئة osg :: Texture2D ما إذا كانت أحجام صور النسيج هي مضاعفات قوة لاثنين (على سبيل المثال ، 64 × 64 أو 256 × 512) ، وتغيير حجم الصور تلقائيًا غير مناسب للحجم ، باستخدام وظيفة OpenGL gluScaleImage () بالفعل. هناك طريقة setResizeNonPowerOfTwoHint () تحدد ما إذا كان يجب تغيير حجم الصورة أم لا. تتطلب بعض بطاقات الفيديو مضاعفات حجم صورة قوة اثنين ، بينما تدعم فئة osg :: Texture2D العمل بحجم نسيج تعسفي.

شيء عن مزج الملمس


كما قلنا من قبل ، يتم تطبيع إحداثيات النسيج من 0 إلى 1. يتوافق النقطة (0 ، 0) مع الزاوية العليا اليسرى من الصورة ، والنقطة (1 ، 1) تتوافق مع أسفل اليمين. ماذا يحدث إذا قمت بتعيين إحداثيات نسيج أكبر من واحد؟

افتراضيًا ، في OpenGL ، كما هو الحال في OSG ، سيتم تكرار النسيج في اتجاه المحور ، وستتجاوز قيمة إحداثيات النسيج واحدًا. غالبًا ما تستخدم هذه التقنية ، على سبيل المثال ، لإنشاء نموذج لجدار من الطوب الطويل ، أستخدم نسيجًا صغيرًا ، وأكرر تراكبه عدة مرات في العرض والارتفاع.

يمكن التحكم في هذا السلوك من خلال طريقة setWrap () لفئة osg :: Texture. كمعلمة أولى ، تأخذ الطريقة معرف المحور الذي يجب تطبيق وضع المزج عليه ، وتمريره كمعلمة ثانية ، على سبيل المثال

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

يخبر هذا الرمز صراحة المحرك بتكرار النسيج على طول المحورين s و r إذا تجاوزت قيم تنسيق النسيج 1. قائمة كاملة من أوضاع تعيين النسيج:

  1. كرر - كرر الملمس.
  2. مرآة - كرر الملمس ، وعكسها.
  3. CLAMP_TO_EDGE - يتم إحداثيات الإحداثيات التي تتجاوز 0 إلى 1 إلى الحافة المقابلة من النسيج.
  4. CLAMP_TO_BORDER - الإحداثيات التي تتجاوز 0 إلى 1 ستمنح لون الحدود الذي حدده المستخدم.

4. تقديم إلى الملمس


تسمح تقنية تقديم المادة للمطور بإنشاء نسيج يستند إلى مشهد أو نموذج فرعي ثلاثي الأبعاد وتطبيقه على السطح في المشهد الرئيسي. وغالبا ما تسمى تقنية مماثلة الملمس "الخبز".

لخبز مادة بشكل ديناميكي ، يجب عليك إكمال ثلاث خطوات:

  1. قم بإنشاء كائن نسيج لعرضه.
  2. جعل المشهد في نسيج.
  3. استخدام الملمس الناتج على النحو المنشود.

يجب علينا إنشاء كائن نسيج فارغ. يسمح لك OSG بإنشاء نسيج فارغ بحجم معين. تتيح لك طريقة setTextureSize () ضبط عرض القامة وارتفاعها ، وكذلك العمق كمعلمة إضافية (للأنسجة ثلاثية الأبعاد).

للتقديم إلى نسيج ، يجب إرفاقه بكائن الكاميرا عن طريق استدعاء طريقة attach () ، والتي تأخذ كائن نسيج كوسيطة. بالإضافة إلى ذلك ، تقبل هذه الطريقة وسيطة تشير إلى أي جزء من إطار المخزن المؤقت يجب تقديمه في هذا النسيج. على سبيل المثال ، لنقل مخزن مؤقت للون إلى نسيج ، يجب تشغيل التعليمات البرمجية التالية

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

تتضمن الأجزاء الأخرى من المخزن المؤقت للإطار المتوفرة للتقديم المخزن المؤقت عمق DEPTH_BUFFER ، المخزن المؤقت STENCIL_BUFFER المخزن المؤقت ، ومخازن لون إضافية من COLOR_BUFFER0 إلى COLOR_BUFFER15. يتم تحديد وجود مخازن مؤقتة للألوان وعددها حسب طراز بطاقة الفيديو.

بالإضافة إلى ذلك ، بالنسبة للكاميرا التي يتم تحويلها إلى نسيج ، يجب عليك تعيين معلمات مصفوفة العرض وطريقة العرض التي يتناسب حجمها مع حجم النسيج. سيتم تحديث النسيج كما يتم رسم كل إطار. ضع في اعتبارك أنه لا ينبغي استخدام الكاميرا الرئيسية للتجسيد في نسيج ، حيث إنها توفر تجسيدًا للمشهد الرئيسي وستحصل على شاشة سوداء فقط. قد لا يتحقق هذا المطلب فقط عند إجراء عرض خارج الشاشة.

5. مثال على تنفيذ التقديم على الملمس


لشرح أسلوب العرض في نسيج ما ، ننفذ المهمة التالية: إنشاء مربع ، ورسم نسيج مربع عليه ، وجعل مشهدًا متحركًا في النسيج ، بالطبع مع سيسنا الذي نحبه. البرنامج الذي ينفذ المثال ضخم للغاية. ومع ذلك ، سأقدم لك شفرة المصدر الكاملة على أي حال.

مثال Texrender
الرئيسية

 #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; } 


لإنشاء مربع ، اكتب وظيفة حرة منفصلة

 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(); } 

تقبل الوظيفة موضع مركز المربع وأبعاده الهندسية كمدخل. ثم يتم إنشاء مجموعة من القمم ومجموعة من الحالات الطبيعية وإحداثيات النسيج ، وبعد ذلك يتم إرجاع الهندسة التي تم إنشاؤها من الوظيفة.

في جسم البرنامج الرئيسي ، قم بتحميل طراز سيسنا

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

من أجل تحريك هذا النموذج ، قم بإنشاء وتهيئة تحول الدوران حول المحور Z

 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)); 

إنشاء نسيج فارغ لمربع 1024 × 1024 بكسل بتنسيق RGBA بكسل (ألوان ثلاثية المكونات 32 بت مع قناة ألفا)

 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()); 

يشير ترتيب العرض الخاص بـ PRE_RENDER إلى أن هذه الكاميرا يتم عرضها قبل عرضها في المشهد الرئيسي. حدد FBO كهدف للعرض وإرفاق نسيجنا بالكاميرا. الآن قمنا بإعداد الكاميرا للعمل في نظام الإحداثيات المطلقة ، وكمشهد قمنا بتعيين الشجرة الفرعية الخاصة بنا ، والتي نريد تحويلها إلى نسيج: تحويل الدوران مع نموذج سيسنا متصل به

 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); 

أخيرًا ، حرك المشهد وعرضه ، مع تغيير زاوية دوران الطائرة حول المحور Z في كل إطار

 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; } 

نتيجة لذلك ، نحصل على صورة مثيرة للاهتمام إلى حد ما



في هذا المثال ، طبقنا بعض الرسوم المتحركة للمشهد ، لكن تذكر أن توسيع حلقة التشغيل () وتغيير معلمات العرض قبل أو بعد عرض الإطار هو نشاط غير آمن من حيث تنظيم الوصول إلى البيانات من تدفقات مختلفة. نظرًا لأن OSG يستخدم التجسيد متعدد الخيوط ، فهناك أيضًا آليات منتظمة لتضمين تصرفاته الخاصة في عملية التقديم ، والتي توفر وصولاً آمنًا للخيوط إلى البيانات.

6. حفظ نتيجة العرض إلى ملف


يدعم OSG القدرة على إرفاق كائن osg :: Image بالكاميرا وحفظ محتويات المخزن المؤقت للإطار في المخزن المؤقت لبيانات الصورة. بعد ذلك ، من الممكن حفظ هذه البيانات على القرص باستخدام الدالة osg :: writeImageFile ()

 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" ); 

الخاتمة


ربما يبدو أن المواد المقدمة في المقال تافهة. ومع ذلك ، فهي تحدد أساسيات العمل مع القوام في OpenSceneGraph ، والتي تستند إليها تقنيات أكثر تعقيدًا للعمل مع هذا المحرك ، والتي سنتحدث عنها بالتأكيد في المستقبل.

أن تستمر ...

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


All Articles