
مقدمة
واحدة من أكثر المهام إثارة للاهتمام التي يتم حلها عن طريق الرسومات ثلاثية الأبعاد هي إنشاء "عوالم كبيرة" - مشاهد طويلة تحتوي على عدد كبير من الكائنات مع إمكانية حركة غير محدودة حول المسرح. يعتمد حل هذه المشكلة على القيود المفهومة الكامنة في أجهزة الكمبيوتر.
مثال نموذجي: "العالم الكبير" عند تصور السكك الحديدية على محرك OSG. كل ما هو مفقود هو أنجلوليرس يلتهم العالم وراء القطار ...في هذا الصدد ، تنشأ الحاجة لإدارة موارد التطبيق ، والتي تتلخص في حل واضح: تحميل فقط تلك الموارد (النماذج ، القوام ، وما إلى ذلك) اللازمة لتشكيل مشهد في الوقت الحالي بالموقع الحالي للمراقب ؛ تخفيض مستويات تفاصيل الكائنات عن بعد ؛ تفريغ الكائنات لم تعد هناك حاجة من ذاكرة النظام. بالنسبة للجزء الأكبر ، توفر محركات الرسومات والألعاب مجموعة معينة من الأدوات لحل مثل هذه المشكلات. اليوم ننظر إلى أي منها متاح في OpenSceneGraph.
1. باستخدام مستويات التفاصيل (LOD)
تتيح لك تقنية استخدام مستويات التفاصيل عرض الكائن نفسه بتفاصيل أكثر أو أقل ، اعتمادًا على المسافة منه إلى المراقب. يعتمد استخدام هذه التقنية على اعتبار بسيط هو أن التفاصيل الصغيرة لنموذج ثلاثي الأبعاد لا يمكن تمييزها على مسافة كبيرة ، مما يعني أنه لا توجد حاجة لرسمها. من ناحية ، تسمح لك هذه التقنية بتقليل العدد الإجمالي لمخرجات بدائية هندسية إلى المخزن المؤقت للإطار ، ومن ناحية أخرى ، لا تفقد نطاق عرض كائنات المشهد ، وهو أمر مفيد للغاية عند إنشاء عوالم مفتوحة كبيرة.
يوفر OSG أدوات لتطبيق هذه التقنية من خلال فئة osg :: LOD ، الموروثة من نفس osg :: Group. يسمح لك هذا الفصل بتمثيل الكائن نفسه في عدة مستويات من التفاصيل. يتميز كل مستوى من التفاصيل بمسافة الحد الأدنى والحد الأقصى للمراقب ، حيث يتم تبديل عرض الكائن في هذا المستوى من التفاصيل.
يسمح لك osg :: LOD بتحديد هذا النطاق فورًا عند تحديد عقدة تابعة ، أو أحدث ، باستخدام أساليب setRange ()
osg::ref_ptr<osg::LOD> lodNode = new osg::LOD; lodNode->addChild(node2, 500.0f, FLT_MAX); lodNode->addChild(node1); . . . lodNode->setRange(1, 0.0f, 500.0f);
نستمر في تعذيب سيسنا وتوضيح التقنية الموصوفة مع مثال
مثال اللدالرئيسية #ifndef MAIN_H #define MAIN_H #include <osg/LOD> #include <osgDB/ReadFile> #include <osgUtil/Simplifier> #include <osgViewer/Viewer> #endif
الرئيسية #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> modelL3 = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Node> modelL2 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::ref_ptr<osg::Node> modelL1 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL)); osgUtil::Simplifier simplifer; simplifer.setSampleRatio(0.5f); modelL2->accept(simplifer); simplifer.setSampleRatio(0.1f); modelL1->accept(simplifer); osg::ref_ptr<osg::LOD> root = new osg::LOD; root->addChild(modelL1.get(), 200.0f, FLT_MAX); root->addChild(modelL2.get(), 50.0f, 200.0f); root->addChild(modelL3.get(), 0.0f, 50.0f); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
أولاً ، قم بتحميل النموذج
osg::ref_ptr<osg::Node> modelL3 = osgDB::readNodeFile("../data/cessna.osg");
تحتاج الآن إلى إنشاء عدة نماذج (سنقتصر على مثالين) ، مع مستوى أقل من التفاصيل. للقيام بذلك ، انسخ العقدة التي تم تحميلها مرتين ، وذلك باستخدام تقنية ما يسمى بالنسخة "العميقة" للفئة ، للعقدة التي تم تنفيذها بواسطة طريقة clone ()
osg::ref_ptr<osg::Node> modelL2 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::ref_ptr<osg::Node> modelL1 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL));
الآن نقوم بتقليل هندسة هذه النماذج باستخدام فئة osgUtil :: Simplifer. يتم تعيين درجة تبسيط النموذج من خلال طريقة setSampleRatio () من هذه الفئة - فكلما كانت المعلمة أصغر ، كلما كان النموذج أقل تفصيلًا بعد تطبيق إجراء التخفيض
osgUtil::Simplifier simplifer; simplifer.setSampleRatio(0.5f); modelL2->accept(simplifer); simplifer.setSampleRatio(0.1f); modelL1->accept(simplifer);
عندما يكون لدينا نماذج بمستويات مختلفة من التفاصيل ، فيمكننا شحنها إلى عقدة الجذر ، والتي تم إنشاؤها كمؤشر ذكي لـ osg :: LOD. لكل مستوى من التفاصيل ، قم بتعيين مسافة العرض لهذا المستوى
osg::ref_ptr<osg::LOD> root = new osg::LOD; root->addChild(modelL1.get(), 200.0f, FLT_MAX); root->addChild(modelL2.get(), 50.0f, 200.0f); root->addChild(modelL3.get(), 0.0f, 50.0f);
بواسطة FLT_MAX يعني بطريقة أو بأخرى مسافة كبيرة "بلا حدود" للمراقب. بعد بدء العارض ، نحصل على الصورة التالية
مستوى التفاصيل 3

مستوى التفاصيل 2

مستوى التفاصيل 1

يمكن ملاحظة أنه عندما يتم نقل الكاميرا بعيدًا عن الكائن ، تنخفض تفاصيل الشكل الهندسي المعروض. باستخدام هذه التقنية ، يمكنك تحقيق واقعية عالية للمشهد مع انخفاض استهلاك الموارد.
2. تقنية تحميل الخلفية للعقد المشهد
يوفر محرك OSG فئتي osg :: ProxyNode و osg :: PagedLOD ، المصممة لموازنة الحمل عند تقديم المشهد. كلا الفئتين ترث من osg :: Group.
تقلل عقدة نوع osg :: ProxyNode من وقت تشغيل التطبيق قبل التقديم ، إذا كان المشهد يحتوي على عدد كبير من الطرز التي تم تحميلها وعرضها من القرص. إنه يعمل كواجهة للملفات الخارجية ، مما يسمح بالتحميل المؤجل للنماذج. لإضافة العقد الفرعية ، استخدم طريقة setFileName () (بدلاً من addChild) لتعيين اسم ملف النموذج على القرص وتحميله ديناميكيًا.
ترث عقدة osg :: PagedNode طرق osg :: LOD وتحميل وتفريغ مستويات التفاصيل بطريقة لتجنب التحميل الزائد لخط أنابيب OpenGL وضمان التقديم السلس للمشهد.
3. دينامية (وقت التشغيل) تحميل النموذج
دعونا نرى كيف تحدث عملية تحميل النموذج باستخدام osg :: ProxyNode.
مثال Proxynodeالرئيسية #ifndef MAIN_H #define MAIN_H #include <osg/ProxyNode> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::ProxyNode> root = new osg::ProxyNode; root->setFileName(0, "../data/cow.osg"); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
عملية التنزيل هنا مختلفة قليلاً
osg::ref_ptr<osg::ProxyNode> root = new osg::ProxyNode; root->setFileName(0, "../data/cow.osg");
بدلاً من تحميل نموذج البقرة بشكل صريح ، نشير إلى عقدة الجذر اسم الملف الذي يوجد به النموذج وفهرس العقدة الفرعية ، حيث يجب وضع هذا النموذج بعد تحميله. عند تنفيذ البرنامج ، نحصل على هذه النتيجة

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

ما يحدث في المثال أعلاه؟ osg :: ProxyNode و osg :: PagedLOD تعمل في هذه الحالة كحاويات. سيرسل مدير البيانات الداخلية لـ OSG الطلبات وتحميل البيانات في الرسم البياني للمشهد كلما دعت الحاجة إلى ملفات النماذج ومستويات التفاصيل.
تعمل هذه الآلية في عدة تدفقات خلفية وتتحكم في تحميل البيانات الثابتة الموجودة في الملفات الموجودة على القرص والبيانات الديناميكية التي تم إنشاؤها وإضافتها أثناء تنفيذ البرنامج.
يقوم المحرك تلقائيًا بمعالجة العقد التي لا يتم عرضها في إطار العرض الحالي وإزالتها من الرسم البياني للمشهد عندما يكون التحميل في وضع زائد. ومع ذلك ، لا يؤثر هذا السلوك على العقد osg :: ProxyNode.
مثل العقدة الوكيل ، لدى الفئة osg :: PagedLOD أيضًا طريقة setFileName () لتحديد المسار إلى النموذج الذي تم تحميله ، ومع ذلك ، يجب عليك تعيين نطاق الرؤية لها ، كما هو الحال مع عقدة osg :: LOD. شريطة أن يكون لدينا ملف cessna.osg ونموذج بولي منخفض المستوى L1 ، يمكننا تنظيم العقدة المقسمة إلى صفحات كما يلي
osg::ref_ptr<osg::PagedLOD> pagedLOD = new osg::PagedLOD; pagedLOD->addChild(modelL1, 200.0f, FLT_MAX ); pagedLOD->setFileName( 1, "cessna.osg" ); pagedLOD->setRange( 1, 0.0f, 200.0f );
يجب أن تفهم أنه لا يمكن إلغاء تحميل عقدة modelL1 من الذاكرة ، نظرًا لأن هذه هي عقدة تابعة غير تابعة للطفل عادية.
عند التقديم ، لا يكون الفرق بين osg :: LOD و osg :: PagedLOD مرئيًا من الخارج ، إذا كنت تستخدم مستوى واحد فقط من تفاصيل النموذج. ستكون فكرة مثيرة للاهتمام تنظيم مجموعة كبيرة من طرازات Cessna باستخدام فئة osg :: MatrixTransform. لهذا ، يمكنك استخدام على سبيل المثال هذه الوظيفة
osg::Node* createLODNode( const osg::Vec3& pos ) { osg::ref_ptr<osg::PagedLOD> pagedLOD = new osg::PagedLOD; … osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform; mt->setMatrix( osg::Matrix::translate(pos) ); mt->addChild( pagedLOD.get() ); return mt.release(); }
برنامج مثال ينفذ تحميل خلفية 10000 طائرة
الرئيسية #ifndef MAIN_H #define MAIN_H #include <osg/PagedLOD> #include <osg/MatrixTransform> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
من المفترض أن تكون الطائرة موجودة على متن طائرة بفاصل 50 وحدة إحداثية. عند التحميل ، سنرى أنه يتم تحميل فقط تلك سيسنا التي تدخل في الإطار. تلك الطائرات التي تختفي من الإطار تختفي من شجرة المشهد.

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