مرحبا بالجميع! أنا قائد الفريق لتطوير تطبيقات سطح المكتب في شركة Rogia Europe. نحن نطور حلول البرمجيات لصناعة النفط والغاز.
لقد حدث أن منتجنا الرائد StarSteer لا يحتوي على لوحة ارتباط - وهي أداة كلاسيكية لإرشادات جيدة. تم تأجيل المهمة لفترة طويلة بسبب مهام أخرى أكثر أولوية ، لكن في الخريف الماضي تمكنا أخيرًا من البدء بها.
تجاوز أسئلة قاعدة الشفرة القديمة - سأذكرها في المقال - كان هناك سؤال أساسي واحد - ما هي التكنولوجيا التي يجب استخدامها؟ من المؤكد أننا نحتاج إلى OpenGL - والذي يتم استخدامه بالفعل في عرض MapView و 3 d على أساس OpenSceneGraph - لكن من الواضح أنه ليس مكشوفًا ، مع عناصر واجهة رسومية. سقط OSG = (. تقنية تلبي متطلبين - رسم بياني للمشهد و GUI على OpenGL - كنت أعرف واحدًا فقط - Qt QML / Quick. حول ما حصلنا عليه وما يجب مشاركته - من الداخل.
دخول
بدأنا تطوير المنتج في خريف عام 2013. ما مجموعة المكتبات لاستخدامها بالنسبة لي ، كمشجع كيو تي ، لم يكن حتى السؤال. في ذلك الوقت ، قد يطرح السؤال: استخدم الإصدار الرابع أو الخامس (الذي لا يزال حديثًا إلى حد ما) من Qt. لقد اخترنا الخامس ومن ذروة رحلتنا لا أستطيع إلا أن أقول: الحمد لله!
تم تطوير مظهر كامل على QtGui / الحاجيات. جميع المشاهد التي تعرض فيها الرسوم البيانية (جاما ، المسامية ، المقاومة ، إلخ) مصنوعة على QGraphicsScene / View. نصيحتي هي - لا تستخدم هذه الحزمة للأشياء الخطيرة! الوسيطات: قم بتمرير أشرطة غير مفصولة من الخارج (خارجها بدون تعديلات Qt الخاصة ) لمنطقة المشهد ( qgraphicsview.cpp +458 وما فوقها بنفس الطريقة للأفقية ؛ qgraphicsview.cpp +3816 - ما التحكم في المصفوفة). إذا لم يزعجك ذلك ، فاستخدم - الكثير من القطع المريحة من الصندوق.
ماذا لا تستخدم؟ NSIS.
كان كل شيء مثاليًا ، وكان المنتج قيد التطوير ، والمهام قيد التنفيذ ، وكان عدد العملاء ينمو. إعادة بيع ... بشكل عام ، بعد مرور بعض الوقت ، بدأت QGraphicsScene تتداخل معنا - لم نكن مستعجلين لتحسين رسم الرسوم البيانية البالغة 40.000 نقطة ، عندما تم تشغيل الخطوط السميكة - كانت كل هذه الأشياء على وحدة المعالجة المركزية بطيئة للغاية.
على طول الطريق ، سئمنا من تطوير GUY على الحاجيات. إما بيديك بالكامل في الكود ، أو قليلاً في المصمم (من Creator ، .ui + .cpp). أردت الأشياء الحديثة ، مثل الوصف التعريفي للواجهة الرسومية.
قدم قائمة من التقنيات التي سنفعل بها:
- الطريقة القديمة على QGraphicsView / Scene ؛
- لكل مسار استخدام QOpenGLWidget العارية منفصلة ؛
- تطبيق النافذة بأكملها على QOpenGLWidget ، ولكن واجهة المستخدم الرسومية من جانبنا (أو العثور على شيء) ؛
- النقطتين السابقتين + OSG على التوالي ؛
- Qt QML / Quick ،
ست نقاط فقط ؛ ناقشنا. تفوقت الكاريزما الخاصة بي وقررت محاولة معرفة كيف يتصرف النموذج الأولي في QML.
النموذج
لقد فتحت مثال scenegraph \ graph. نظرت وأغلقت =). لقد لعبت لعدة أيام ، ونظرت في أمثلة أخرى ، لكن لا شيء يقترب من هدفي العزيزة.
وما المطلوب إذن؟ إليك ما قد تبدو عليه لوحة الارتباط:
بنية بسيطة إلى حد ما هي قائمة من مسارات الآبار ، حيث تتتبع المنحنيات والمقاييس. حسنا ، الأشياء الصغيرة. الكثير من النص ، في المستقبل ، بعض عناصر التحكم - الأزرار ، والقوائم المنسدلة ، وحقول الإدخال ، وأكثر من ذلك.
نظرت إلى sgengine ، تعلمت كيفية إنشاء رسم بياني للمشهد ورسمهما في إطار العرض المعين. فيما بعد أدركت أنه مع هذا الخيار ، لن يقوم QML / Quick ، فلماذا أحتاج كل هذا؟
في الحقيقة ، لا أتذكر الأدوية ، لكنني قررت لسبب ما اللجوء إلى أساسيات رسومات الحاسوب. لذلك في المراحل الأخيرة من التنقيط ، تتم ترجمة جميع إحداثيات المشهد إلى NKU (إحداثيات الجهاز المعيارية ؛ والمعروفة باسم NDC = إحداثيات الجهاز المعياري). نعم ، سمعت أن إخراج تظليل قمة الرأس هو في الواقع مساحة مقطع وبعد ذلك لا يزال هناك تشويه تقارب ، ولكن كل هذا للتمثيل ثلاثي الأبعاد ، وفي 2D يوجد دائمًا w = 1 وبالتالي يمكننا افتراض أن الإخراج على الفور إلى NDC.
حسنًا ، NDC ، ما هو التالي؟ إذا كان عرض نافذتك هو 800 بكسل ، فإن إحداثيات NDC لمركز بكسل صفر هي -1 ؛ الإحداثي المركزي لل 799 هو 1. باختصار ، ndcX = -1 + 2 * i / 799. الآن تخيل أن هناك مستطيلًا من 100 إلى 300 وأرغب في رسم المشهد بأكمله ليس في نافذة كاملة ، ولكن إلى ذلك. باستخدام هذه المعرفة المجزأة ، سأحسب ndcX100 ، ndcX300 ، ثم أرميهم في تظليل قمة الرأس وهناك ، بعد المعيار
gl_Position = matrix * position;
الخطي "التفاف" gl_Position.x في [ndcX100 ؛ ndcX300]. نحن نفعل الشيء نفسه بالنسبة للمكون العمودي. ستتيح لك هذه الخدعة تفرخ المشاهد في أي مستطيل محدد للمشهد. مع هذه المعرفة ، بدأ المثال البياني للخضوع للتغييرات. يمكنك إلقاء نظرة على الرعية هنا - الرسم البياني . جميع الملح في shaders/line.vsh
.
مشهد / مشهد
كانت الأشهر الثلاثة التالية كتابة المعارف التقليدية ، واتضح أن 12 ورقة من A4 =). في موازاة ذلك ، نظرنا في الهندسة المعمارية. لقد أخذوا MVC ... إنه MVP ... إنه متسلسل هرمي MVC / MVP ... أو حتى PAC - كل هذه اصطلاحات ، والتحلل الجيد مهم.
بشكل عام ، قمنا بإعداد مثال للمشهد. المصادر متوفرة هنا - SceneSample . لقد تحولت إلى إطار عمل معين لإنشاء تطبيقات مع الرسوم البيانية على QtQML / Quick. من فضلك لا تنسى أن هذا الرمز لا يزال يعمل كمثال. نعم ، بالفعل نصف جاهز ويبدو أكثر أو أقل أنيق ، ولكن ليس جاهزًا.
المشهد هو اللاعب الرئيسي. يراقب هذا الفصل إحداثيات NDC ويقوم بتحديث المصفوفات المقابلة. SceneCamera هو الأصدقاء المقربين معه. الكيان التالي الجدير بالذكر هو SceneItem. لا فائدة منه في حد ذاته ، فهو يحتوي فقط على بعض المنطق الأساسي ؛ يرثها - مثل LineStrip - وتنفيذ ما تحتاجه. في الوقت نفسه ، في updatePaintNode
تحتاج إلى استخدام مشتقات SceneMaterial - FlatColorMaterial كمرجع. الكيانات المتبقية أيضا تفعل شيئا =) ، وجميع أنواع المتلاعبين ، والأدوات. لا يتم طرح العديد من الفئات في QML ، ولا يمكنك الاستغناء عن مثل C ++ ؛ هل تتذكر ما هو غير جاهز؟
الصعوبة الثانية هي أننا إذا أردنا استخدام عناصر التحكم داخل مشهد جديد ، فلن نتمكن من القيام بذلك. فكرنا ، قررنا أننا لسنا في حاجة إليها ، وواصلنا التطوير بهدوء.
مزايا النهج:
- يتم رسم كل شيء في عمود واحد من المشهد ؛
- لم نقم بتصحيح Qt - يظل من الممكن إضافة عناصر تحكم QML منتظمة إلى المسرح بحيث يكون الترتيب العيني بينها والمنحنيات (أو SceneItem الأخرى) صحيحًا ؛
- انخفاض استهلاك الذاكرة مقارنة مع الأساليب الأخرى.
سلبيات
- urbermachine المعقدة.
- معرفة OpenGL و GLSL مطلوبة ؛
- الحل شبه النهائي.
بالطبع ، واجهنا بعض الصعوبات أثناء التطوير. كان واحد منهم
علة مع ترتيب z
عندما حاولنا لأول مرة عرض مشهد به منحنيات ، شاهدنا مثل هذه الصور:


للوهلة الأولى ، لم يكن الأمر واضحًا ، "لقد فعلنا كل شيء بشكل صحيح!" لقد خمنت بشكل غامض أن gl_Position.z كانت خاطئة إلى حد ما ، ولكن لماذا كان من الصعب فهم السبب في الليل. لم نستسلم: لقد رأينا أن شركة Qt تقوم بتصحيح التظليل وتضيف الكود لتغيير gl_Position.z ، كما اعتقدنا. بعد فترة من الزمن ، اتضح لنا: لقد أفسدنا بيانات المصفوفة بالتغيير في z ، و Qt تنقل قيمها إليهم! وبالتالي ، يتم تعيين قيمة item.z من QML إلى z من OpenGL ( SceneMaterial.cpp +20 ):

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

قامت Q&A بتعطيل البرنامج ووجدت خطوات للتشغيل المستقر: لقد حددنا التدرج للشاشة بحيث لا يتعدى 100٪ وخطوط "flash". جلس Artyom ، فكر ، ووجد أنه عندما يكون clip: true
والعنصر مستطيلًا ، يتم استخدام glScissor ، لكن حججها هي إحداثيات بكسل عدد صحيح! في عناصر QML تكون حقيقية واتضح أن تنقيط الخط انخفض إلى البيكسل التالي / السابق ، وتم قطع المقص إلى التيار.
تم إصلاح المشهد مثل هذا: width: Math.round(metrics.width + leftPadding + 2 * rightPadding + 0.5)
. وفقًا لذلك ، يجب أن يكون لعنصر المشهد دائمًا إحداثيات عدد صحيح من أجل تجنب مثل هذه القطع الأثرية.
في الختام ، سأحضر KDPV

شكرا لكم جميعا على اهتمامكم!