2012-09-12 16 views
12

現在、Qt3Dモジュールを使用してC++/Qt5でゲームを書いています。Qt3DのQGLView(QGLWidgetではなく)へのオーバーパインティングとポストレンダーの効果

シーン(QGLSceneNodes)をQGLViewにレンダリングできますが、に固執して、いくつかのGUI要素でシーンをオーバーコーティングしています。 QMLやC++を使ってインタフェースのルック&フィールを定義するかどうかはまだ決まっていないので、私は両方のソリューションを公開しています。 (QMLモジュールはQtQuick3Dと呼ばれ、C++モジュールはQt3Dと呼ばれ、Qt5の一部です)

私はQMLベースのソリューションを非常に好んでいます。

私はいくつかのオーバーコーティングを行うことができますか?

次のものが可能でなければならない:

  • は、アルファチャンネルをサポートし、もちろん、所与のスクリーン座標で2Dアイテム(画像)を描画します。
  • システムフォントを使用してテキストを描画します。最初にイメージにペイントし、これをQt3D OpenGLコンテキストのテクスチャとして使用します。
  • 画面座標でのマウス入力に反応します(3Dシーンオブジェクトのピッキングとは対照的に)。

これは、2次元GUIパーツ用にもう1つのQGLSceneNodeを使用してすべて可能であると思います。しかし、レンダリング(頂点シェーダを使用)中に再指向と再配置を行うためにシーンノードを配置して配置することは意味をなさず、数値エラーを招き、非効率的に思えると思います。

別の「GUI」頂点シェーダを使用していますか?

(どのように)レンダリング後のエフェクトとオーバーコーティングの作業を一緒に行うことができますか?

私は次のポストレンダリングエフェクトを行うことができればそれは本当にいいだろう:

  • 、以前にレンダリングされたシーンを読んで、いくつかの2D画像効果を適用する(私はフラグメントシェーダとしてそれらを実装するために想像できるとこのエフェクトシェーダを使用して、最後に表示されたバッファを最終画面にレンダリングします)。これにより、GUIの一部をガラスのようにぼかし効果で表示させることができます。
  • さらに高度なエフェクトを使用すると、シーンをより現実的に見せることができます。奥行き:&モーションブラー、熱依存の空気ちらつき、グロー効果。それらはすべて、レンダリング時にフラグメントシェーダ(質問の一部ではない)と追加のバッファを使用して記述することができます。彼らは、メイン画面のレンダリングプロセスとGUIのオーバーペインティングの間に配置する必要があります、これは私がこの質問に含めた理由です。

私は特別なシェーダプログラムを使用して上塗りを実装する問題を参照してください。私は、GUIは、ガラスのように見えるようにガウスぼかしのようないくつかの効果を適用するためにGUIの後ろのピクセルにアクセスすることはできません。 シーンをQGLViewではなく別のバッファにレンダリングする必要があります。これは私の主な問題です...

+0

私は、レンダーモードをパースペクティブではなく正規直交に設定することができることに気付きました。これにより、GUIシェーダーが不要になります。しかし、私は、Qt3Dでオーバーパインティングを行うための組み込みの方法が必要だと思います。(古いQGLWidgetのメソッドがあるので(これはQt5ウィジェットモジュールにあるので使いたくないのでできませんQGLPainterを使用してレンダリング))。 – leemes

+0

これは可能性が高いようです(かなりいいですね):http://comments.gmane.org/gmane.comp.lib.qt.3d/264 – leemes

答えて

1

私はqglwidgetを使って同様の問題を抱えていました。私が思い描いたソリューションは、正投影を使ってスクラッチコントロールをレンダリングしています。メイン描画関数の最後に、ビューを設定して2dビットを描画する別の関数を呼び出します。マウスイベントに関しては、メインウィジェットに送信されたイベントをキャプチャし、ヒットテスト、再帰座標オフセット、および擬似コールバックの組み合わせを使用して、コントロールをネストすることができます。私の実装では、一度に1つずつトップレベルのコントロール(主にスクロールボックス)にコマンドを転送しますが、コントロールを動的に追加する必要がある場合は、リンクリスト構造を簡単に追加できます。私は現在、部品をカラーパネルとしてレンダリングするためにクワッドを使用していますが、テクスチャを追加したり、ダイナミックライティングに対応する3Dコンポーネントにするのは簡単です。そこのコードの多くはだので、私はちょうどつもり作品に関連するビットを投げています:

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

は、2Dレンダリングのためのモデルビュー/投影行列を設定するコードは次のとおりです。

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

このhudを処理する主な描画関数です。ドロー機能の大部分は大きな「バックラウンドパネル」チャンクに似ています。

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

を今すぐコントロール自身のために:あなたは基本的にはモデルビュー行列を押し、popmatrixその後、コントロールをペイント、あなたが3Dで希望のように/回転/スケールを変換します。ボタン、スライダー、ラベルなどのすべてのコントロールは、空の仮想関数を持つ共通の基底クラスからすべてのものが入っていて、相互に相互作用することができます。ベースには、高さや座標などの典型的なものと、コールバックやテキストレンダリングを処理するためのポインタがあります。また、それがクリックされたかどうかを伝える一般的なhittest(x、y)関数も含まれています。楕円形のコントロールが必要な場合は、これを仮想的にすることができます。スライダーのような化合物のコントロールは複数のボタンがあり、scrollboxesはタイルとスライダーを持っているので、ここでは、基本クラス、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); 
    } 
} 

、それはすべての描画のために再帰的にする必要があります/マウスイベント。各コントロールには、値を渡すジェネリックコールバック関数と独自のアドレスもあります。これにより、親は子どものそれぞれを呼び出して誰が電話しているかを確認し、適切に応答することができます(例:上/下ボタン)。子コントロールは、c/dtorsのnew/deleteを使用して追加されます。また、子コントロールのdraw()関数は、親のdraw()呼び出しの間に呼び出され、すべてが自己完結型であることを可能にします。ここでは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); 
    } 
} 

は、今、私たちは、OpenGLコンテキストにレンダリングする自己完結型のコントロールを持っていることを、すべてのことが必要なのは、トップレベルのコントロールからインターフェイスです例えば、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... 
    } 
} 

全体的に、このシステムは少しハックであり、私は、キーボードのレスポンスやサイズ変更などのいくつかの機能を追加していないが、これらの追加が容易であるべきで、多分コールバックに「イベントタイプ」を必要とする(すなわちマウスまたはキーボード)。あなたはglBaseを一番上のウィジェットにサブクラス化することもできますし、親子のchildcallbackを受け取ることさえ可能にします。それは大変な作業ですが、このシステムでは、バニラC++/OpenGLが必要で、ネイティブのQtのものよりもはるかに少ないCPU /メモリーリソースしか使用しません。それ以上の情報が必要な場合は、尋ねてください。あなたはqglwidget からクラスを継承を行う場合

1

リサイズやイベントは、ここであなたが描いた画像に関しては

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

を開始するのに役立つはずです短いビデオであり、テキストQtが持っている特別に実装するのは簡単です QGLContext( http://qt-project.org/doc/qt-4.8/qglcontext.html)を使用して現在のコンテキストを渡し、ラベル/イメージ/ウィジェットを特定の座標にレンダリングしますが、この関数呼び出しは、そうでない場合はペイントイベントの最後であることを確認してくださいそれは上にはないでしょう

あなたのクラスにQMouseEventヘッダーを含めるようにすると、ウィジェット内でマウスの座標を取得し、protectedイベントmouseMoveEvent(QMouseEvent * event){event-> pos();}をオーバーロードしてopenglに渡すことができます。

関連する問題