
مقدمة
OpenGL ، وهو الواجهة الخلفية لـ OpenSceneGraph ، يستخدم بدائية هندسية (مثل النقاط والخطوط والمثلثات والوجوه المضلعة) لبناء جميع الكائنات في العالم ثلاثي الأبعاد.
يتم تعريف هذه الأوليات من خلال بيانات حول رؤوسها ، والتي تشمل إحداثيات القمم ، والمكونات العادية ، وبيانات اللون ، وإحداثيات النسيج. يتم تخزين هذه البيانات في صفائف خاصة. يمكن تشكيل البدائي ، على سبيل المثال ، عن طريق تحديد الكائنات التي تصفها قائمة بمؤشرات القمة. تسمى هذه الطريقة طريقة صفيف الذروة ، وهي تلغي تخزين الذروات الزائدة في الذاكرة ولها أداء جيد.
بالإضافة إلى ذلك ، يمكن لـ OpenGL استخدام آلية ما يسمى
بقوائم العرض ، عندما يمكن إعادة استخدام المواد الأولية التي تم إعدادها في ذاكرة الفيديو ، مما يسرع بشكل كبير من عرض الكائنات الثابتة.
بشكل افتراضي ، يستخدم OSG أسلوب مصفوفة الذروة وطريقة قائمة العرض لتقديم الشكل الهندسي. ومع ذلك ، قد يتم تغيير استراتيجية العرض ، اعتمادًا على كيفية تقديم بيانات الهندسة. في هذه المقالة ، سنغطي التقنيات الأساسية للعمل مع الهندسة في OSG.
1. فئات الجيود والقابل للرسم
فئة osg :: Geode هي محطة ، تسمى العقدة "ورقة" لشجرة المشهد. لا يمكن أن تحتوي على عُقد فرعية ، ولكنها تحتوي على جميع المعلومات اللازمة لعرض الهندسة. اسمه ، Geode ، هو اختصار لعقدة الهندسة.
يتم تخزين البيانات الهندسية التي سيتم معالجتها بواسطة المحرك في مجموعة كائنات من فئة osg :: Drawable ، تتم إدارتها بواسطة فئة osg :: Geode. فئة osg :: Drawable فئة افتراضية بحتة. عدد من الفئات الفرعية موروثة منه ، وهي نماذج ثلاثية الأبعاد وصور ونصوص تمت معالجتها بواسطة خط أنابيب OpenGL. يشير OSG إلى الرسم باعتباره جميع العناصر التي يمكن أن يرسمها المحرك.
توفر فئة osg :: Geode عددًا من الطرق لإرفاق وفصل السحوبات:
- الأسلوب العام addDrawable () - يمرر المؤشر إلى عنصر قابل للرسم في مثيل لفئة osg :: Geode. يتم التحكم في جميع هذه العناصر بواسطة المؤشرات الذكية osg :: ref_ptr <>.
- يزيل الأسلوب العام removeDrawable () و removeDrawables () الكائن من osg :: Geode ويقلل العدد المرجعي له. يأخذ الأسلوب removeDrawable () مؤشرًا إلى عنصر مهم كمعلمة واحدة ، ويأخذ الأسلوب removeDrawables () معلمتين: الفهرس الأولي وعدد العناصر المراد إزالتها من صفيف كائن osg :: Geode.
- ترجع طريقة getDrawable () مؤشر إلى عنصر في الفهرس الذي تم تمريره كمعلمة.
- ترجع طريقة getNumDrawables () إجمالي عدد العناصر المرفقة بـ osg :: Geode. على سبيل المثال ، لإزالة جميع العناصر من osg :: Geode ، يمكنك استخدام هذا الرمز
geode->removeDrawables(0, geode->getNumDrawables());
2. رسم أشكال بسيطة
يوفر OSG فئة osg :: ShapeDrawable ، التي هي منحدر من فئة osg :: Drawable ومصممة لإنشاء بدائية ثلاثية الأبعاد بسيطة. تتضمن هذه الفئة كائن osg :: Shape الذي يخزن معلومات حول هندسة معينة ومعلمات أخرى. يتم إنشاء المواد الأولية باستخدام طريقة setShape () ، على سبيل المثال
shapeDrawable->setShape(new osg::Box(osg::Vec3(1.0f, 0.0f, 0.0f), 10.0f, 10.0f, 5.0f));
يخلق مربع مستطيل مع مركز هندسي عند النقطة (1.0 ، 0.0 ، 0.0) بعرض وارتفاع 10 وعمق 5 وحدات. تحدد فئة osg :: Vec3 متجهًا في الفضاء ثلاثي الأبعاد (بالإضافة إلى ذلك ، يتم أيضًا تقديم فئات osg :: Vec2 و osg :: Vec4 التي تصف ناقلات البعد المقابل).
يتم تمثيل الأوليات الأكثر شيوعًا في OSG من خلال الفئات osg :: Box و osg :: Capsule و osg :: Cone و osg :: Cylinder و osg :: Sphere.
خذ بعين الاعتبار مثال على تطبيق هذه الآلية.
main.h #ifndef MAIN_H #define MAIN_H #include <osg/ShapeDrawable> #include <osg/Geode> #include <osgViewer/Viewer> #endif // MAIN_H
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::ShapeDrawable> shape1 = new osg::ShapeDrawable; shape1->setShape(new osg::Box(osg::Vec3(-3.0f, 0.0f, 0.0f), 2.0f, 2.0f, 1.0f)); osg::ref_ptr<osg::ShapeDrawable> shape2 = new osg::ShapeDrawable; shape2->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 1.0f)); shape2->setColor(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::ShapeDrawable> shape3 = new osg::ShapeDrawable; shape3->setShape(new osg::Sphere(osg::Vec3(3.0f, 0.0f, 0.0f), 1.0f)); shape3->setColor(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(shape1.get()); root->addDrawable(shape2.get()); root->addDrawable(shape3.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
لا يحتاج هذا المثال بشكل خاص إلى تعليقات: في البرنامج يتم إنشاء ثلاثة أشكال بسيطة ، بعد التجميع والإطلاق سنرى مثل هذه النتيجة

الآلية الموضحة في المثال بسيطة ومباشرة ، ولكنها ليست الطريقة الأكثر فاعلية لإنشاء هندسة ويمكن استخدامها حصريًا للاختبارات. يتم استخدام فئة osg :: Geometry لإنشاء هندسة في التطبيقات عالية الأداء المستندة إلى OSG.
3. تخزين البيانات الهندسية: فئات osg :: Array and osg :: Geometry
فئة osg :: Array هي فئة تجريدية أساسية ، والتي يرث منها العديد من المتحدرين ، مصممة لتخزين البيانات التي يتم تمريرها إلى وظائف OpenGL. يشبه العمل مع هذا الفصل العمل مع std :: vector من مكتبة C ++ القياسية. يوضح الكود التالي إضافة متجه إلى صفيف قمة باستخدام طريقة push_back ()
vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
يتم تخصيص صفائف OSG على كومة الذاكرة المؤقتة وتدار بواسطة مؤشرات ذكية. ومع ذلك ، لا ينطبق هذا على عناصر الصفيف ، مثل osg :: Vec3 أو osg :: Vec2 ، والتي يمكن إنشاؤها أيضًا على المكدس.
فئة osg :: Geometry هي مجمّع فوق دالات OpenGL التي تعمل مع صفائف الرأس. مشتق من فئة osg :: Drawable ويمكن إضافته بسهولة إلى قائمة الكائنات osg :: Geode. تأخذ هذه الفئة المصفوفات الموصوفة أعلاه كمدخل وتستخدمها لإنشاء هندسة باستخدام OpenGL.
4. القمم وسماتها
القمة هي وحدة ذرية من بدائية الهندسة. يحتوي على عدد من السمات التي تصف نقطة في الفضاء ثنائي الأبعاد أو ثلاثي الأبعاد. تتضمن السمات: الموضع واللون والمتجه العادي وإحداثيات النسيج وإحداثيات الضباب وما إلى ذلك. يجب أن يكون للجزء العلوي دائمًا موضع في الفضاء ، أما بالنسبة للسمات الأخرى ، فيمكن أن تكون موجودة اختياريًا. يدعم OpenGL 16 سمة قمة رئيسية ويمكنه استخدام صفائف مختلفة لتخزينها. كل صفائف السمات مدعومة من فئة osg :: Geometry ويمكن ضبطها باستخدام طرق مجموعة النماذج * Array ().
سمات Vertex في OpenSceneGraphالسمة | نوع البيانات | Osg :: طريقة الهندسة | اتصال OpenGL مكافئ |
---|
الموقف | 3-ناقلات | setVertexArray () | مؤشر glVertexPointer () |
عادي | 3-ناقلات | setNormalArray () | glNormalPointer () |
اللون | 4 ناقلات | setColorArray () | مؤشر glColorPointer () |
اللون الثانوي | 4 ناقلات | setSecondaryColorArray () | glSecondaryColorPointerEXT () |
إحداثيات الضباب | تعويم | setFogCoordArray () | glFogCoordPointerEXT () |
إحداثيات الملمس | 2 أو 3 ناقلات | setTexCoordArray () | glTexCoordPointer () |
سمات أخرى | من تحديد المستخدم | setVertexArribArray () | glVertexAttribPointerARB () |
من حيث المبدأ ، من الضروري تعيين سماتك الخاصة لكل من القمم ، مما يؤدي إلى تكوين عدة صفائف من الصفات من نفس الحجم - وإلا ، فإن عدم تطابق أحجام المصفوفات يمكن أن يؤدي إلى سلوك غير محدد للمحرك. يدعم OSG طرقًا مختلفة لربط سمات الرأس ، على سبيل المثال
geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
يعني أن كل قمة وكل لون من قمة الرأس مرتبط واحد لواحد مع بعضها البعض. ومع ذلك ، إذا نظرت إلى هذا الرمز
geom->setColorBinding(osg::Geometry::BIND_OVERALL);
ثم يطبق لونًا واحدًا على الهندسة بأكملها. وبالمثل ، يمكن تكوين العلاقات بين السمات الأخرى عن طريق استدعاء أساليب setNormalBinding () و setSecondaryColorBinding () و setFogCoordBinding () و setVertexAttribBinding ().
5. مجموعات بدائية هندسية
الخطوة التالية بعد تحديد صفائف سمة القمة هي وصف كيفية عرض بيانات القمة. يتم استخدام فئة osg :: PrimitiveSet الافتراضية للتحكم في الأوليات الهندسية الناتجة عن جهاز العرض من مجموعة من القمم. توفر فئة osg :: Geometry عدة طرق للعمل مع مجموعات بدائية هندسية:
- addPrimitiveSet () - يمرر المؤشر إلى مجموعة من الأوليات في كائن osg :: Geometry.
- removePrimitiveSet () - إزالة مجموعة من الأوليات. كمعلمات ، فإنه يأخذ الفهرس الأولي للمجموعات وعدد المجموعات المراد حذفها.
- getPrimitiveSet () - إرجاع مجموعة من الأوليات في الفهرس الذي تم تمريره كمعلمة.
- getNumPrimitiveSets () - تُرجع العدد الإجمالي لمجموعات البدائية المرتبطة بهذه الهندسة.
فئة osg :: PrimitiveSet مجردة ولا يمكن استنساخها ، ولكن العديد من الفئات المشتقة التي تحتوي على مجموعات البدائية التي يعمل OpenGL معها ، مثل osg :: DrawArrays و osg :: DrawElementsUInt ، ترث منه.
تستخدم فئة osg :: DrawArrays عدة عناصر متتالية من صفيف قمة الرأس لبناء بدائية هندسية. يمكن إنشاؤها وإرفاقها بالهندسة عن طريق استدعاء طريقة.
geom->addPrimitiveSet(new osg::DrawArrays(mode, first, count));
يعين وضع المعلمة الأولى النوع البدائي على أنواع OpenGL البدائية المقابلة: GL_POINTS و GL_LINE_STRIP و GL_LINE_LOOP و GL_LINES و GL_TRIANGLE_STRIP و GL_TRIANGLE_FAN و GL_TRIANGLES و GL_QUAD_STRIP و GL_QUADS و GL_POLY.
تحدد المعلمتان الأولى والثانية الفهرس الأول في مصفوفة الذروة وعدد الذروات التي يجب إنشاء الهندسة منها.
علاوة على ذلك ، لا تتحقق OSG مما إذا كان العدد المحدد من القمم كافٍ لبناء الهندسة المحددة بواسطة الوضع ، مما قد يؤدي إلى تعطل التطبيق!6. مثال - رسم مربع مطلي
ننفذ كل ما سبق كمثال بسيط
كود المصدر الكامل للمثال الرباعيmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgViewer/Viewer> #endif // MAIN_H
main.cpp #include "main.h" int main(int argc, char *argv[]) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 1.0f)); vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); colors->push_back(osg::Vec4(1.0f, 1.0f, 1.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->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
بعد التجميع والتنفيذ ، نحصل على نتيجة مشابهة لهذه

يحتاج هذا المثال إلى توضيح. لذا ، بادئ ذي بدء ، نقوم بإنشاء مجموعة من رؤوس المربع ، حيث يتم تخزين إحداثياتها
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 1.0f)); vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
بعد ذلك ، نضع مجموعة المعايير الطبيعية. في حالتنا البسيطة ، لا نحتاج إلى إنشاء حالة طبيعية لكل قمة - ما عليك سوى وصف متجه وحدة واحد موجه بشكل متعامد على مستوى المربع
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
عيّن لونًا لكل قمة
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
الآن قم بإنشاء كائن هندسي حيث سيتم تخزين وصف المربع الخاص بنا ، والذي سيتم معالجته بواسطة التقديم. نمرر مجموعة من القمم لهذه الهندسة
osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get());
من خلال تمرير مجموعة من المعايير الطبيعية ، نبلغ المحرك بأنه سيتم استخدام واحد عادي لجميع القمم ، من خلال الإشارة إلى طريقة الربط ("ملزم") للمعايير العادية BIND_OVAERALL
quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL);
على عكس ألوان القمم ، على العكس ، نشير إلى أن كل قمة سيكون لها لونها الخاص
quad->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
الآن إنشاء مجموعة من البدائية للهندسة. نشير إلى أنه يجب إنشاء الوجوه المربعة (GL_QUADS) من مصفوفة الذروة ، مع أخذ الرأس مع الفهرس 0 باعتباره الرأس الأول ، وسيكون إجمالي عدد القمم 4
quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
حسنًا ، أعتقد أنه لا يستحق شرح نقل الهندسة وبدء العرض
osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();
الرمز أعلاه يكافئ التصميم التالي في OpenGL النقي
static const GLfloat vertices[][3] = { … }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 4, GL_FLOAT, 0, vertices ); glDrawArrays( GL_QUADS, 0, 4 );
7. فهرسة الذروات في الأوليات
تعمل فئة osg :: DrawArrays بشكل جيد عند قراءة بيانات الرأس مباشرة من المصفوفات ، بدون فجوات. ومع ذلك ، لا يكون هذا فعالًا جدًا عندما يمكن أن ينتمي الرأس نفسه إلى عدة وجوه لكائن. دعونا نلقي نظرة على مثال.

يحتوي المكعب على ثمانية رؤوس. ومع ذلك ، كما يتبين من الشكل (ننظر إلى تتكشف المكعب على متن الطائرة) تنتمي بعض القمم إلى أكثر من وجه واحد. إذا قمت ببناء مكعب مكون من 12 وجهًا مثلثيًا ، فسيتم تكرار هذه القمم ، وبدلاً من مجموعة من 8 رؤوس ، نحصل على مجموعة من 36 قمة ، معظمها في الواقع نفس الرأس!
فئات OSG osg :: DrawElementsUInt و osg :: DrawElementsUByte و osg :: DrawElementsUShort ، التي تستخدم صفائف فهرس القمة كبيانات ، مصممة لحل المشكلة الموضحة. تقوم مصفوفات الفهارس بتخزين فهارس رؤوس البدائيين التي تصف الوجوه وعناصر الهندسة الأخرى. عند تطبيق هذه الفئات على مكعب ، يكفي تخزين مجموعة من ثمانية رؤوس مرتبطة بالوجوه من خلال صفائف من المؤشرات.
يتم إنشاء فئات من النوع osg :: DrawElements * بالطريقة نفسها المتبعة في class std :: vector القياسي. يمكن استخدام هذا الرمز لإضافة المؤشرات.
osg::ref_ptr<osg::DrawElementsUInt> de = new osg::DrawElementsUInt(GL_TRIANGLES); de->push_back(0); de->push_back(1); de->push_back(2); de->push_back(3); de->push_back(0); de->push_back(2);
يحدد هذا الرمز الوجه الأمامي للمكعب الموضح في الشكل.
دعونا نفكر في مثال توضيحي آخر - ثماني السطوح

إنه مثير للاهتمام لأنه يحتوي على ستة رؤوس فقط ، لكن كل قمة تدخل ما يصل إلى أربعة وجوه مثلثة! يمكننا إنشاء مجموعة من 24 قمة لعرض جميع الوجوه الثمانية باستخدام osg :: DrawArrays. ومع ذلك ، سنفعل خلاف ذلك - سنقوم بتخزين القمم في مصفوفة من ستة عناصر ، وننشئ وجوهًا باستخدام فئة osg :: DrawElementsUInt.
مصدر كامل لمثال الثماني السطوحmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgUtil/SmoothingVisitor> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(6); (*vertices)[0].set( 0.0f, 0.0f, 1.0f); (*vertices)[1].set(-0.5f, -0.5f, 0.0f); (*vertices)[2].set( 0.5f, -0.5f, 0.0f); (*vertices)[3].set( 0.5f, 0.5f, 0.0f); (*vertices)[4].set(-0.5f, 0.5f, 0.0f); (*vertices)[5].set( 0.0f, 0.0f, -1.0f); osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_TRIANGLES, 24); (*indices)[ 0] = 0; (*indices)[ 1] = 1; (*indices)[ 2] = 2; (*indices)[ 3] = 0; (*indices)[ 4] = 4; (*indices)[ 5] = 1; (*indices)[ 6] = 4; (*indices)[ 7] = 5; (*indices)[ 8] = 1; (*indices)[ 9] = 4; (*indices)[10] = 3; (*indices)[11] = 5; (*indices)[12] = 3; (*indices)[13] = 2; (*indices)[14] = 5; (*indices)[15] = 1; (*indices)[16] = 5; (*indices)[17] = 2; (*indices)[18] = 3; (*indices)[19] = 0; (*indices)[20] = 2; (*indices)[21] = 0; (*indices)[22] = 3; (*indices)[23] = 4; osg::ref_ptr<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray(vertices.get()); geom->addPrimitiveSet(indices.get()); osgUtil::SmoothingVisitor::smooth(*geom); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(geom.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
دعونا نحلل هذا الرمز بمزيد من التفصيل. بالطبع ، أول شيء نقوم به هو إنشاء مجموعة من ستة رؤوس
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(6); (*vertices)[0].set( 0.0f, 0.0f, 1.0f); (*vertices)[1].set(-0.5f, -0.5f, 0.0f); (*vertices)[2].set( 0.5f, -0.5f, 0.0f); (*vertices)[3].set( 0.5f, 0.5f, 0.0f); (*vertices)[4].set(-0.5f, 0.5f, 0.0f); (*vertices)[5].set( 0.0f, 0.0f, -1.0f);
نقوم بتهيئة كل قمة مباشرة عن طريق الوصول إلى متجه إحداثياته باستخدام عملية إلغاء الإشارة لعامل المؤشر والعامل [] (نتذكر أن osg :: Array مشابه في جهازه إلى std :: vector).
الآن قم بإنشاء الوجوه كقائمة بمؤشرات القمة
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_TRIANGLES, 24); (*indices)[ 0] = 0; (*indices)[ 1] = 1; (*indices)[ 2] = 2;
ستكون الوجوه ثلاثية ، سيكون هناك 8 ، مما يعني أن قائمة المؤشرات يجب أن تحتوي على 24 عنصرًا. تنتقل مؤشرات الوجوه بالتسلسل في هذا الصفيف: على سبيل المثال ، يتم تشكيل face 0 بواسطة القمم 0 و 1 و 2 ؛ وجه 1 - القمم 0 و 4 و 1 ؛ وجه 2 - القمم 4 و 5 و 1 وهكذا. يتم سرد القمم بترتيب بعكس اتجاه عقارب الساعة إذا نظرت إلى وجه الوجه (انظر الشكل أعلاه).
مزيد من الخطوات لإنشاء الهندسة التي قمنا بها في الأمثلة السابقة. الشيء الوحيد الذي لم نفعله هو التوليد التلقائي للمعايير العادية (المتوسطة) السلسة ، والتي نقوم بها في هذا المثال من خلال استدعاء
osgUtil::SmoothingVisitor::smooth(*geom);
في الواقع ، إذا تم إعطاء رؤوس الوجه ، فمن السهل حساب المعدل الطبيعي لها. في القمم التي تتلاقى فيها عدة وجوه ، يتم حساب متوسط طبيعي معين - تتم إضافة القيم الطبيعية للوجبات المتقاربة ويتم تسوية المجموع الناتج مرة أخرى. يمكن تنفيذ هذه العمليات (بالإضافة إلى المزيد!) بواسطة المحرك نفسه باستخدام فئات من مكتبة osgUtil. لذلك ، في مثالنا ، سنضيف تعليمات إلى الرابط لإنشاء برنامجنا مع هذه المكتبة في ملف * .pro
octahedron.pro CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) . . . LIBS += -L$$OSG_LIB_DIRECTORY -losgUtild } else { . . . LIBS += -L$$OSG_LIB_DIRECTORY -losgUtil }
نتيجة لذلك ، نحصل على النتيجة التالية

لفهم كيفية عمل ذلك ، ضع في اعتبارك خط أنابيب OpenGL

تقلل آلية صفيف الرأس من عدد مكالمات OpenGL. يقوم بتخزين بيانات الرأس في ذاكرة التطبيق ، والتي يتم استخدامها على جانب العميل. يحصل خط أنابيب OpenGL على جانب الخادم على الوصول إلى صفائف قمة مختلفة. كما هو موضح في الرسم التخطيطي ، يتلقى OpenGL البيانات من المخزن المؤقت قمة الرأس على جانب العميل ، وبطريقة مرتبة ، يقوم بتجميع البدائيين. هذه هي الطريقة التي تتم بها معالجة البيانات باستخدام أساليب set * Array () للفئة osg :: Geometry. يمر الصف osg :: DrawArrays عبر هذه الصفائف مباشرة ويعرضها.
عند استخدام osg :: DrawElements * ، يتم تقليل أبعاد صفائف الذروة ويتم تقليل عدد الرؤوس المنقولة إلى خط الأنابيب. تسمح لك مجموعة من المؤشرات بإنشاء ذاكرة تخزين مؤقت للرأس على جانب الخادم. يقرأ OpenGL بيانات الرأس من ذاكرة التخزين المؤقت ، بدلاً من القراءة من ذاكرة التخزين المؤقت للرأس على جانب العميل. هذا يزيد بشكل كبير من أداء العرض العام.
8. تقنيات معالجة شبكة مضلع
يدعم OpenSceneGraph تقنيات مختلفة لمعالجة الشبكة المضلعة لكائنات هندسة المشهد. غالبًا ما تُستخدم طرق المعالجة المسبقة ، مثل تقليل المضلع والفسيفساء ، لإنشاء النماذج المضلعة وتحسينها. لديهم واجهة بسيطة ، لكنهم في هذه العملية يقومون بالكثير من الحسابات المعقدة وليست مناسبة جدًا للتنفيذ الفوري.
تشمل التقنيات الموضحة ما يلي:
- osgUtil :: Simplifier - تقليل عدد المثلثات في الهندسة. يتم استخدام الطريقة العامة () لتبسيط هندسة النموذج.
- osgUtil :: SmootingVisitor - حساب المعايير الطبيعية. يمكن استخدام الطريقة الملساء () لتوليد معايير عادية ممهدة للنموذج ، بدلاً من حسابها بشكل مستقل ووضعها بشكل صريح من خلال مجموعة من المعايير.
- osgUtil :: TangentSpaceGenerator - توليد ناقلات أساس المماس لرؤوس النماذج. يتم تشغيله عن طريق استدعاء الأسلوب توليد () وحفظ النتيجة التي تم إرجاعها بواسطة الأساليب getTangentArray () و getNormalArray () و getBinormalArray (). يمكن استخدام هذه النتائج لسمات قمة مختلفة عند كتابة تظليل في GLSL.
- osgUtil :: Tesselator - يقوم بعمل التغطية بالفسيفساء لشبكة مضلع - يقوم بتقسيم الأوليات المعقدة إلى سلسلة من تلك البسيطة (retesselatePolygons () method)
- osgUtil :: TriStripVisitor - يحول سطحًا هندسيًا إلى مجموعة من شرائط الوجوه المثلثية ، مما يتيح العرض مع استهلاك فعال للذاكرة. تحول طريقة stripify () مجموعة من البدائل النموذجية إلى شكل هندسي بناءً على مجموعة GL_TRIANGLE_STRIP.
تقبل جميع الطرق هندسة الكائن كمعلمة تم تمريرها بواسطة osg :: Geometry & link ، على سبيل المثال مثل هذا
osgUtil::TriStripVisitor tsv; tsv.stripify(*geom);
حيث تشير geom إلى مثيل من الهندسة موصوف بواسطة مؤشر ذكي.
الطبقات osg :: Simplifier، osg :: SmoothingVisitor و osg :: TriStripVisitor يمكن أن تعمل مباشرة مع العقد في الرسم البياني للمشهد ، على سبيل المثال
osgUtil::TriStripVisitor tsv; node->accept(tsv);
يعالج أسلوب Accept () جميع العقد الفرعية حتى يتم تطبيق العملية المحددة على جميع العقد الطرفية لهذا الجزء من شجرة المشهد المخزنة في العقد من نوع osg :: Geode.
لنجرب تقنية التغطية بالفسيفساء عمليًا.
رمز المثال الكامل tesselatormain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgUtil/Tessellator> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) ); // 0 vertices->push_back( osg::Vec3(2.0f, 0.0f, 0.0f) ); // 1 vertices->push_back( osg::Vec3(2.0f, 0.0f, 1.0f) ); // 2 vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.0f) ); // 3 vertices->push_back( osg::Vec3(1.0f, 0.0f, 2.0f) ); // 4 vertices->push_back( osg::Vec3(2.0f, 0.0f, 2.0f) ); // 5 vertices->push_back( osg::Vec3(2.0f, 0.0f, 3.0f) ); // 6 vertices->push_back( osg::Vec3(0.0f, 0.0f, 3.0f) ); // 7 osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray(vertices.get()); geom->setNormalArray(normals.get()); geom->setNormalBinding(osg::Geometry::BIND_OVERALL); geom->addPrimitiveSet(new osg::DrawArrays(GL_POLYGON, 0, 8)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(geom.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
استنادًا إلى الوضع المكاني للقمم في هذا المثال ، نرى أننا نحاول إنشاء مضلع غير محدب من ثمانية رؤوس باستخدام جيل واحد من نوع GL_POLYGON. يوضح التجميع والتنفيذ لهذا المثال أن النتيجة التي نتوقعها لا تعمل - يتم عرض المثال بشكل غير صحيح

لإصلاح هذه المشكلة ، يجب أن تكون الهندسة التي تم إنشاؤها مرصوفة قبل تمريرها إلى العارض
osgUtil::Tessellator ts; ts.retessellatePolygons(*geom);
وبعد ذلك نحصل على النتيجة الصحيحة

كيف يعمل؟ لن يتم عرض المضلع غير المحدب ، بدون استخدام التغطية بالفسيفساء الصحيحة ، كما نتوقع ، نظرًا لأن OpenGL ، الذي يسعى إلى تحسين الأداء ، سيعتبره مضلعًا بسيطًا ومحدبًا أو يتجاهله ببساطة ، وهو ما يمكن أن يعطي نتائج غير متوقعة تمامًا.
تستخدم فئة osgUtil :: Tessellator خوارزميات لتحويل مضلع محدب إلى سلسلة من غير محدبة - في حالتنا ، تحول الهندسة إلى GL_TRIANGLE_STRIP.
يمكن أن تتعامل هذه الفئة مع مضلعات الثقب والمضلعات المتقاطعة ذاتيًا. باستخدام طريقة setWindingType () العامة ، يمكنك تحديد قواعد معالجة متنوعة ، مثل GLU_TESS_WINDING_ODD أو GLU_TESS_WINDING_NONZERO ، والتي تحدد المناطق الداخلية والخارجية لمضلع معقد.الخلاصة
في هذه المقالة ، حصلنا على فهم أساسي لكيفية تخزين ومعالجة هندسة الكائنات ثلاثية الأبعاد في محرك OSG. لا تعتقد أن تلك الأمثلة البسيطة وغير المثيرة للإعجاب التي يتم تناولها في المقالة هي حدود قدرات المحرك. فقط هذه الأمثلة يمكن أن تساعد المطور على فهم آليات OpenSceneGraph ، وبدون هذا الفهم يكون من الصعب تخيل عمل أشياء أكثر تعقيدًا.تستند هذه المقالة على ترجمة نص الفصول المقابلة من كتاب OpenSceneGraph 3.0 ومعالجته. دليل المبتدئين . يتم فحص جميع الأمثلة من قبلي شخصيًا ، ويتوفر رمز المصدر الخاص بهم هنا . يتبع ...