2012-09-12 19 views
12

में QGLView (QGLWidget नहीं) पर ओवरपेनटिंग और पोस्ट-रेंडर प्रभाव मैं वर्तमान में Qt3D मॉड्यूल का उपयोग कर C++/Qt5 में एक गेम लिख रहा हूं।क्यू 3 डी

मैं एक QGLView पर दृश्य (QGLSceneNodes) प्रस्तुत कर सकता हूं लेकिन अब कुछ GUI तत्वों के साथ दृश्य को ओवरपेनटिंग पर पर अटक गया हूं। मैंने अभी तक तय नहीं किया है कि इंटरफ़ेस के स्वरूप और अनुभव को परिभाषित करने के लिए QML या C++ का उपयोग करना है, इसलिए मैं दोनों के लिए समाधान के लिए खुला हूं। (ध्यान दें कि क्यूएमएल मॉड्यूल को क्यूटीक्विक 3 डी कहा जाता है, सी ++ मॉड्यूल को क्यूटी 3 डी कहा जाता है और वे दोनों क्यूटी 5 का हिस्सा हैं।)

मैं क्यूएमएल आधारित समाधान को बहुत पसंद करता हूं।

मैं कुछ ओवरपेनटिंग कैसे कर सकता हूं?

निम्नलिखित बातें संभव होना जरूरी:

  • अल्फा चैनलों के लिए समर्थन के साथ दिए गए स्क्रीन निर्देशांक पर 2 डी वस्तुओं (चित्र) ड्रा, निश्चित रूप से।
  • सिस्टम फोंट का उपयोग करके टेक्स्ट ड्रा करें, शायद किसी छवि पर पहली पेंटिंग करके और इसे Qt3D OpenGL संदर्भ में बनावट के रूप में उपयोग करें।
  • स्क्रीन निर्देशांक में माउस इनपुट पर प्रतिक्रिया (3 डी दृश्य ऑब्जेक्ट पिकिंग के विपरीत)।

मुझे लगता है कि 2 डी जीयूआई भाग के लिए बस एक और QGLSceneNode का उपयोग करना संभव है। लेकिन मुझे लगता है कि रेंडरिंग (वर्टेक्स शेडर का उपयोग करके) फिर से उन्मुख और फिर से स्थानांतरित करने के लिए कुछ दृश्य नोड को पोजिशन करना और उन्मुख करना, समझ में नहीं आता है, संख्यात्मक त्रुटियों को प्रस्तुत करता है और अक्षम लगता है।

कोई अन्य "जीयूआई" वर्टेक्स शेडर सही तरीके से उपयोग कर रहा है?

(कैसे) मैं पोस्ट-रेंडरिंग प्रभाव और एक साथ काम को ओवरपेनटिंग कर सकता हूं?

यह वास्तव में अच्छा होगा अगर मैं निम्नलिखित के बाद प्रतिपादन प्रभाव कर सकते हैं:

  • पहले से प्रदान की गई दृश्य पढ़ें, कुछ 2 डी छवि प्रभाव लागू (मैं एक टुकड़ा शेडर के रूप में उन को लागू करने की कल्पना कर सकते हैं और इस प्रभाव शेडर का उपयोग कर अंतिम स्क्रीन पर पहले प्रस्तुत किए गए बफर को प्रस्तुत करें)। यह मुझे जीयूआई के कुछ हिस्सों को धुंध प्रभाव के साथ ग्लास की तरह दिखने में सक्षम बनाता है।
  • कुछ और उन्नत प्रभाव दृश्य को अधिक यथार्थवादी लग सकते हैं: गहराई & गति धुंध, गर्मी निर्भर वायु झिलमिलाहट, चमक प्रभाव। वे सभी को एक टुकड़ा शेडर (जो प्रश्न का हिस्सा नहीं होना चाहिए) और प्रतिपादन समय के दौरान एक अतिरिक्त बफर का उपयोग करके वर्णित किया जा सकता है। उन्हें मुख्य स्क्रीन प्रतिपादन प्रक्रिया और जीयूआई ओवरपेनटिंग के बीच रखा जाना चाहिए, यही कारण है कि मैंने इसे इस प्रश्न में शामिल किया।

मैं जब मैं overpainting एक विशेष शेडर प्रोग्राम का उपयोग लागू कोई समस्या देखते हैं: मैं जीयूआई पीछे पिक्सल उपयोग नहीं कर सकते कांच की तरह जीयूआई देखो बनाने के लिए गाऊसी कलंक की तरह कुछ प्रभाव लागू करने के लिए। मुझे दृश्य को QGLView के बजाय किसी अन्य बफर में प्रस्तुत करना होगा। यह मेरी मुख्य समस्या है ...

+0

मुझे अभी एहसास हुआ कि मैं केवल परिप्रेक्ष्य के बजाय रेंडर मोड को ऑर्थोनॉर्मल पर सेट कर सकता हूं। यह जीयूआई शेडर अनावश्यक बनाता है। लेकिन फिर भी, मुझे लगता है कि Qt3D में ओवरपेनटिंग करने के लिए एक अंतर्निहित तरीका होना चाहिए (क्योंकि पुराने पुराने QGLWidget में एक विधि है (जिसे मैं Qt5 विजेट मॉड्यूल में उपयोग करने के बाद से उपयोग नहीं करना चाहता हूं और नहीं कर सकता QGLPainter का उपयोग करके प्रस्तुत करें))। – leemes

+0

यह एक संभावना प्रतीत होता है (यह बहुत अच्छा लगता है): http://comments.gmane.org/gmane.comp.lib.qt.3d/264 – leemes

उत्तर

1

मेरे पास एक ओपनगल गेम बनाने के साथ-साथ qglwidget के समान समस्याएं थीं, और जिस समाधान के साथ मैं आया था वह स्क्रैच-निर्मित नियंत्रण प्रदान करने के लिए ऑर्थोग्राफिक प्रक्षेपण का उपयोग कर रहा है। मुख्य ड्रॉ फ़ंक्शन के अंत में, मैं एक अलग फ़ंक्शन को कॉल करता हूं जो दृश्य सेट करता है और फिर 2 डी बिट्स खींचता है। माउस घटनाओं के लिए, मैं मुख्य विजेट पर भेजे गए कार्यक्रमों को कैप्चर करता हूं, फिर हिट टेस्ट, रिकर्सिव समन्वय ऑफसेट्स और छद्म-कॉलबैक के संयोजन का उपयोग करता हूं जो मेरे नियंत्रण को घोंसला करने की अनुमति देता है। मेरे कार्यान्वयन में, मैं एक समय में शीर्ष स्तर नियंत्रण (मुख्य रूप से स्क्रॉल बॉक्स) पर आदेश अग्रेषित करता हूं, लेकिन यदि आप गतिशील रूप से नियंत्रण जोड़ना चाहते हैं तो आप आसानी से एक लिंक-सूची संरचना जोड़ सकते हैं। मैं वर्तमान में रंगीन पैनलों के रूप में भागों को प्रस्तुत करने के लिए क्वाड का उपयोग कर रहा हूं, लेकिन यह बनावट जोड़ने या उन्हें 3 डी घटकों को भी बनाना चाहिए जो गतिशील प्रकाश का जवाब देते हैं। वहाँ कोड का एक बहुत कुछ है, तो मैं कर रहा हूँ बस वाला टुकड़ों में प्रासंगिक बिट फेंक:

void GLWidget::drawOverlay() { 
    glClear(GL_DEPTH_BUFFER_BIT); 

    glPushMatrix(); //reset 
    glLoadIdentity(); //modelview 

    glMatrixMode(GL_PROJECTION);//set ortho camera 
    glPushMatrix(); 
    glLoadIdentity(); 
    gluOrtho2D(0,width()-2*padX,height()-2*padY,0); // <--critical; padx/y is just the letterboxing I use 
    glMatrixMode(GL_MODELVIEW); 

    glDisable(GL_DEPTH_TEST); // <-- necessary if you want to draw things in the order you call them 
    glDisable(GL_LIGHTING); // <-- lighting can cause weird artifacts 

    drawHUD(); //<-- Actually does the drawing 

    glEnable(GL_LIGHTING); 
    glEnable(GL_DEPTH_TEST); 

    glMatrixMode(GL_PROJECTION); glPopMatrix(); 
    glMatrixMode(GL_MODELVIEW); glPopMatrix(); 
} 

यह:

void GLWidget::paintGL() { 
    if (isPaused) return; 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

    glPushMatrix(); 

    glLightfv(GL_LIGHT0, GL_POSITION, FV0001); 

    gluLookAt(...); 

    // Typical 3d stuff 

    glCallList(Body::glGrid); 

    glPopMatrix(); 

//non3d bits 
    drawOverlay(); // <---call the 2d rendering 

    fpsCount++; 
} 

यहाँ कोड है कि 2 डी प्रतिपादन के लिए modelview/प्रक्षेपण मैट्रिक्स सेट करता है मुख्य ड्राइंग फ़ंक्शन है जो हड सामान को संभालता है; अधिकांश ड्रॉ फ़ंक्शंस बड़े 'बैकराउंड पैनल' खंड के समान होते हैं। आप मूल रूप से modelview मैट्रिक्स धक्का, जैसे आप 3 डी में होगा अनुवाद कर/बारी बारी से/पैमाने का उपयोग करें, नियंत्रण रंग, तो popmatrix: नियंत्रण के लिए खुद को

void GLWidget::drawHUD() { 
    float gridcol[] = { 0.5, 0.1, 0.5, 0.9 }; 
    float gridcolB[] = { 0.3, 0.0, 0.3, 0.9 }; 

//string caption 
    QString tmpStr = QString::number(FPS); 
    drawText(tmpStr.toAscii(),120+padX,20+padY); 

//Markers 
    tGroup* tmpGroup = curGroup->firstChild; 

    while (tmpGroup != 0) { 
    drawMarker(tmpGroup->scrXYZ); 
    tmpGroup = tmpGroup->nextItem; 
    } 

//Background panel 
    glEnable(GL_BLEND); 
    glTranslatef(0,height()*0.8,0); 
    glBegin(GL_QUADS); 
    glColor4fv(gridcol); 
    glVertex2f(0.0f, 0.0); 
    glVertex2f(width(), 0); 
    glColor4fv(gridcolB); 
    glVertex2f(width(), height()*0.2); 
    glVertex2f(0.0f, height()*0.2); 
    glEnd(); 
    glDisable(GL_BLEND); 

    glLoadIdentity(); //nec b/c of translate ^^ 
    glTranslatef(-padX,-padY,0); 

//Controls 
    cargoBox->Draw(); 
    sideBox->Draw(); 
    econBox->Draw(); 
} 

अब। मेरे सभी नियंत्रण जैसे बटन, स्लाइडर्स, लेबल इत्यादि सभी खाली वर्चुअल फ़ंक्शंस वाले सामान्य बेस क्लास से वंचित होते हैं जो उन्हें एक-दूसरे के साथ-साथ अज्ञात रूप से बातचीत करने की अनुमति देते हैं। बेस में सामान्य सामान जैसे ऊंचाई, निर्देशांक, आदि के साथ-साथ कॉलबैक और टेक्स्ट प्रतिपादन को संभालने के लिए कुछ पॉइंटर्स भी होते हैं। जेनेरिक हिटेस्ट (एक्स, वाई) फ़ंक्शन भी शामिल है जो बताता है कि इसे क्लिक किया गया है या नहीं। यदि आप अंडाकार नियंत्रण चाहते हैं तो आप यह आभासी बना सकते हैं। यहाँ आधार वर्ग, hitTest, और एक सरल बटन .:

class glBase { 
public: 
    glBase(float tx, float ty, float tw, float th); 

    virtual void Draw() {return;} 

    virtual glBase* mouseDown(float xIn, float yIn) {return this;} 
    virtual void mouseUp(float xIn, float yIn) {return;} 
    virtual void mouseMove(float xIn, float yIn) {return;} 

    virtual void childCallBack(float vIn, void* caller) {return;} 

    bool HitTest(float xIn, float yIn); 

    float xOff, yOff; 
    float width, height; 

    glBase* parent; 

    static GLWidget* renderer; 
protected: 
    glBase* callFwd; 
}; 


bool glBase::HitTest(float xIn, float yIn) { //input should be relative to parents space 
    xIn -= xOff; yIn -= yOff; 
    if (yIn >= 0 && xIn >= 0) { 
    if (xIn <= width && yIn <= height) return true; 
    } 
    return false; 
} 

class glButton : public glBase { 
public: 
    glButton(float tx, float ty, float tw, float th, char rChar); 

    void Draw(); 

    glBase* mouseDown(float xIn, float yIn); 
    void mouseUp(float xIn, float yIn); 
private: 
    bool isPressed; 
    char renderChar; 
}; 

glButton::glButton(float tx, float ty, float tw, float th, char rChar) : 
glBase(tx, ty, tw, th), isPressed(false), renderChar(rChar) {} 

void glButton::Draw() { 
    float gridcolA[] = { 0.5, 0.6, 0.5, 1.0 };//up 
    float gridcolB[] = { 0.2, 0.3, 0.2, 1.0 }; 
    float gridcolC[] = { 1.0, 0.2, 0.1, 1.0 };//dn 
    float gridcolD[] = { 1.0, 0.5, 0.3, 1.0 }; 
    float* upState; 
    float* upStateB; 

    if (isPressed) { 
    upState = gridcolC; 
    upStateB = gridcolD; 
    } else { 
    upState = gridcolA; 
    upStateB = gridcolB; 
    } 

    glPushMatrix(); 
    glTranslatef(xOff,yOff,0); 

    glBegin(GL_QUADS); 
    glColor4fv(upState); 
    glVertex2f(0, 0); 
    glVertex2f(width, 0); 
    glColor4fv(upStateB); 
    glVertex2f(width, height); 
    glVertex2f(0, height); 
    glEnd(); 

    if (renderChar != 0) //center 
    renderer->drawChar(renderChar, (width - 12)/2, (height - 16)/2); 

    glPopMatrix(); 
} 

glBase* glButton::mouseDown(float xIn, float yIn) { 
    isPressed = true; 
    return this; 
} 

void glButton::mouseUp(float xIn, float yIn) { 
    isPressed = false; 
    if (parent != 0) { 
    if (HitTest(xIn, yIn)) parent->childCallBack(0, this); 
    } 
} 

के बाद से स्लाइडर्स की तरह यौगिक नियंत्रण एकाधिक बटन है, और scrollboxes टाइल्स और स्लाइडर है, यह सब ड्राइंग के लिए पुनरावर्ती होने की जरूरत है के लिए कोड है/माउस घटनाओं। प्रत्येक नियंत्रण में एक सामान्य कॉलबैक फ़ंक्शन भी होता है जो मान और उसके स्वयं के पते को पास करता है; यह अभिभावक को यह देखने के लिए अपने प्रत्येक बच्चे का परीक्षण करने की अनुमति देता है कि कौन कॉल कर रहा है, और उचित प्रतिक्रिया दें (उदाहरण के लिए ऊपर/नीचे बटन)। ध्यान दें कि सी/डाटर में नए/हटाए जाने के माध्यम से बाल नियंत्रण जोड़े जाते हैं। इसके अलावा, बच्चों के नियंत्रण के लिए ड्रा() कार्यों को माता-पिता के अपने ड्रॉ() कॉल के दौरान बुलाया जाता है, जो सबकुछ स्वयं निहित होने की अनुमति देता है। यहाँ एक मध्य स्तर स्लाइडबार कि 3 उप बटन होते हैं से प्रासंगिक कोड है:

glBase* glSlider::mouseDown(float xIn, float yIn) { 
    xIn -= xOff; yIn -= yOff;//move from parent to local coords 
    if (slider->HitTest(xIn,yIn-width)) { //clicked slider 
    yIn -= width; //localize to field area 
    callFwd = slider->mouseDown(xIn, yIn); 
    dragMode = true; 
    dragY = yIn - slider->yOff; 
    } else if (upButton->HitTest(xIn,yIn)) { 
    callFwd = upButton->mouseDown(xIn, yIn); 
    } else if (downButton->HitTest(xIn,yIn)) { 
    callFwd = downButton->mouseDown(xIn, yIn); 
    } else { 
    //clicked in field, but not on slider 
    } 

    return this; 
}//TO BE CHECKED (esp slider hit) 

void glSlider::mouseUp(float xIn, float yIn) { 
    xIn -= xOff; yIn -= yOff; 
    if (callFwd != 0) { 
    callFwd->mouseUp(xIn, yIn); //ok to not translate b/c slider doesn't callback 
    callFwd = 0; 
    dragMode = false; 
    } else { 
    //clicked in field, not any of the 3 buttons 
    } 
} 

void glSlider::childCallBack(float vIn, void* caller) { //can use value blending for smooth 
    float tDelta = (maxVal - minVal)/(10); 

    if (caller == upButton) {//only gets callbacks from ud buttons 
    setVal(Value - tDelta); 
    } else { 
    setVal(Value + tDelta); 
    } 
} 

अब जब हम आत्म निहित नियंत्रण है कि एक ओपन संदर्भ में प्रस्तुत करना है, यह सब आवश्यक है शीर्ष स्तर के नियंत्रण से इंटरफेस है , उदाहरण के लिए glwidget से माउस घटनाओं।

void GLWidget::mousePressEvent(QMouseEvent* event) { 
    if (cargoBox->HitTest(event->x()-padX, event->y()-padY)) { //if clicked first scrollbox 
    callFwd = cargoBox->mouseDown(event->x()-padX, event->y()-padY); //forward the click, noting which last got 
    return; 
    } else if (sideBox->HitTest(event->x()-padX, event->y()-padY)) { 
    callFwd = sideBox->mouseDown(event->x()-padX, event->y()-padY); 
    return; 
    } else if (econBox->HitTest(event->x()-padX, event->y()-padY)) { 
    callFwd = econBox->mouseDown(event->x()-padX, event->y()-padY); 
    return; 
    } 

    lastPos = event->pos(); //for dragging 
} 

void GLWidget::mouseReleaseEvent(QMouseEvent *event) { 
    if (callFwd != 0) { //Did user mousedown on something? 
    callFwd->mouseUp(event->x()-padX, event->y()-padY); 
    callFwd = 0; //^^^ Allows you to drag off a button before releasing w/o triggering its callback 
    return; 
    } else { //local 
    tBase* tmpPick = pickObject(event->x(), event->y()); 
    //normal clicking, game stuff... 
    } 
} 

कुल मिलाकर इस प्रणाली एक छोटे से hacky है, और मैं नहीं जोड़ा है कुंजीपटल प्रतिक्रिया या आकार बदलने जैसी कुछ सुविधाओं, लेकिन इन, जोड़ने के लिए हो सकता है कॉलबैक में एक 'घटना प्रकार' की आवश्यकता होती है आसान होना चाहिए (यानी माउस या कीबोर्ड)। आप ग्लोबबेस को सबसे ऊपर-विजेट में भी उपclass कर सकते हैं, और यह माता-पिता को प्राप्त करने की अनुमति भी देगा-> बालकॉलबैक। यद्यपि यह बहुत काम है, इस प्रणाली को केवल वेनिला सी ++/ओपनजीएल की आवश्यकता होती है और यह देशी क्यूटी सामान की तुलना में बहुत कम सीपीयू/मेमोरी संसाधनों का उपयोग करता है, शायद परिमाण या दो के क्रम से। अगर आपको और जानकारी चाहिए, तो बस पूछें।

1

आकार और घटनाओं विशेष रूप से लागू करने के लिए आसान है, तो आप qglwidget से एक वर्ग इनहेरिट कर रहे हैं यहाँ एक छोटी वीडियो की मदद करनी चाहिए कि आप ड्राइंग छवियों

https://www.youtube.com/watch?v=1nzHSkY4K18

संबंध में

आरंभ है और पाठ क्यूटी है qpainter जो आपको ऐसा करने में मदद कर सकता है, वर्तमान संदर्भ को पास करने के लिए QGLContext (http://qt-project.org/doc/qt-4.8/qglcontext.html) का उपयोग करें और विशिष्ट कॉर्डिनेट पर अपना लेबल/छवि/विजेट प्रस्तुत करें, लेकिन सुनिश्चित करें कि यह फ़ंक्शन कॉल आपके पेंट ईवेंट में आखिरी है यह शीर्ष पर नहीं होगा

माउस ईवेंट पर प्रतिक्रिया करने के लिए अपनी कक्षा में QMouseEvent शीर्षलेख शामिल है, तो आप विजेट के भीतर माउस के निर्देशांक प्राप्त कर सकते हैं और संरक्षित ईवेंट mouseMoveEvent (QMouseEvent * event) {event-> pos();} को ओवरलोड करके ओपनएल में भेज सकते हैं।}