2012-09-12 22 views
12

Tôi hiện đang viết một trò chơi bằng C++/Qt5 bằng mô-đun Qt3D.Hiệu ứng quá tải và sau khi render trên QGLView (không phải QGLWidget) trong Qt3D

Tôi có thể hiển thị cảnh (QGLSceneNodes) trên QGLView nhưng hiện bị kẹt trên ghi đè lên cảnh bằng một số yếu tố GUI. Tôi vẫn chưa quyết định có nên sử dụng QML hay C++ để định nghĩa giao diện của giao diện, vì vậy tôi mở ra các giải pháp cho cả hai. (Lưu ý rằng mô-đun QML được gọi là QtQuick3D, mô-đun C++ được gọi là Qt3D và cả hai đều là một phần của Qt5.)

Tôi rất thích một giải pháp dựa trên QML.

Làm cách nào để tôi có thể thực hiện quá mức?

Những điều sau đây phải có thể:

  • Vẽ mục 2D (hình ảnh) tại tọa độ màn hình nhất định, tất nhiên với sự hỗ trợ cho các kênh alpha.
  • Vẽ văn bản bằng cách sử dụng phông chữ hệ thống, có thể bằng cách vẽ đầu tiên trên hình ảnh và sử dụng kết cấu này trong ngữ cảnh Qt3D OpenGL.
  • Phản ứng khi nhập chuột vào tọa độ màn hình (trái ngược với việc chọn đối tượng cảnh 3D).

Tôi nghĩ rằng điều này là tất cả có thể chỉ sử dụng một QGLSceneNode khác cho phần GUI 2D. Nhưng tôi nghĩ rằng định vị và định hướng một số nút cảnh để chỉ được định hướng lại và định vị lại trong khi hiển thị (sử dụng đổ bóng đỉnh) không có ý nghĩa, giới thiệu các lỗi số và có vẻ không hiệu quả.

Có sử dụng trình đổ bóng đỉnh "GUI" khác theo đúng cách không?

(Làm thế nào) tôi có thể tạo hiệu ứng hậu hình ảnh và làm việc quá mức với nhau?

Nó sẽ thực sự tốt đẹp nếu tôi có thể làm như sau render các hiệu ứng sau:

  • Đọc cảnh render trước đó, áp dụng một số hiệu ứng hình ảnh 2D (Tôi có thể tưởng tượng để thực hiện những như một Shader đoạn và làm cho bộ đệm được hiển thị trước đó trên màn hình cuối cùng bằng cách sử dụng hiệu ứng đổ bóng này). Điều này cho phép tôi làm cho một số phần của GUI trông giống như kính với hiệu ứng mờ.
  • Một số hiệu ứng nâng cao hơn có thể làm cho cảnh trông thực tế hơn: độ sâu & mờ chuyển động, nhấp nháy không khí phụ thuộc nhiệt, hiệu ứng ánh sáng. Tất cả chúng có thể được mô tả bằng cách sử dụng một shader fragment (không nên là một phần của câu hỏi) và một bộ đệm bổ sung trong thời gian render. Chúng nên được đặt giữa quá trình hiển thị màn hình chính và quá nhiều GUI, đây là lý do tại sao tôi đưa nó vào câu hỏi này.

Tôi thấy vấn đề khi triển khai quá mức bằng chương trình đổ bóng đặc biệt: Tôi không thể truy cập các pixel phía sau GUI để áp dụng một số hiệu ứng như gaussian blur để làm cho giao diện trông giống như kính. Tôi sẽ phải hiển thị cảnh đến bộ đệm khác thay vì trên QGLView. Đây là vấn đề chính của tôi ...

+0

Tôi vừa nhận ra rằng tôi chỉ có thể đặt chế độ hiển thị thành trực giao thay vì phối cảnh. Điều này làm cho GUI shader không cần thiết. Nhưng vẫn còn, tôi nghĩ rằng có phải là một cách tích hợp để làm overpainting trong Qt3D (như có một phương pháp trong QGLWidget cũ tốt (mà tôi không muốn sử dụng vì nó trong module widget Qt5 và không thể render bằng QGLPainter)). – leemes

+0

Điều này có vẻ là một khả năng (nghe có vẻ khá hay): http://comments.gmane.org/gmane.comp.lib.qt.3d/264 – leemes

Trả lời

1

Tôi cũng gặp các sự cố tương tự khi tạo trò chơi opengl qua qglwidget và giải pháp mà tôi đưa ra là sử dụng phép chiếu chính xác để hiển thị các điều khiển được tạo bằng đầu. Ở phần cuối của chức năng vẽ chính, tôi gọi một hàm riêng biệt để thiết lập khung nhìn và sau đó vẽ các bit 2d. Đối với các sự kiện chuột, tôi nắm bắt các sự kiện được gửi đến tiện ích chính, sau đó sử dụng kết hợp các lần kiểm tra lượt truy cập, các tọa độ đệ quy và các cuộc gọi lại giả cho phép các điều khiển của tôi được lồng vào nhau. Trong quá trình thực hiện, tôi chuyển tiếp các lệnh tới các điều khiển cấp cao nhất (chủ yếu là các hộp cuộn) một lần, nhưng bạn có thể dễ dàng thêm cấu trúc danh sách liên kết nếu bạn cần thêm các điều khiển động. Tôi hiện đang chỉ sử dụng quads để render các phần như các bảng màu, nhưng nó sẽ được đơn giản để thêm kết cấu hoặc thậm chí làm cho chúng thành phần 3d phản ứng với ánh sáng năng động. Có rất nhiều mã, vì vậy tôi chỉ sẽ ném các bit tương ứng trong phần:

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

Dưới đây là đoạn code mà thiết lập ma trận modelview/chiếu cho dựng hình 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(); 
} 

này là chức năng vẽ chính xử lý các nội dung hud; hầu hết các chức năng vẽ giống với đoạn 'backround panel' lớn. Về cơ bản bạn đẩy ma trận modelview, sử dụng dịch/xoay/quy mô như bạn sẽ trong 3d, vẽ các điều khiển, sau đó 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(); 
} 

Bây giờ cho các điều khiển bản thân. Tất cả các điều khiển của tôi như nút bấm, thanh trượt, nhãn, v.v. đều không hút thuốc từ một lớp cơ sở chung với các chức năng ảo trống cho phép chúng tương tác với nhau theo kiểu agnostically. Các cơ sở có các công cụ điển hình như chiều cao, tọa độ, vv cũng như một vài gợi ý để xử lý các cuộc gọi lại và hiển thị văn bản. Cũng bao gồm hàm hittest chung (x, y) cho biết nếu nó được nhấp vào. Bạn có thể làm điều này ảo nếu bạn muốn điều khiển elip. Đây là lớp cơ sở, các hitTest, và mã cho một nút đơn giản .:

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

Kể từ khi điều khiển kép như thanh trượt có nhiều nút, và scrollboxes có gạch và thanh trượt, tất cả cần phải được đệ quy cho các bản vẽ/sự kiện chuột. Mỗi điều khiển cũng có một hàm gọi lại chung mà chuyển một giá trị và đó là địa chỉ riêng; điều này cho phép phụ huynh kiểm tra từng đứa trẻ của nó để xem ai đang gọi và trả lời thích hợp (ví dụ: nút lên/xuống). Lưu ý rằng điều khiển con được thêm vào thông qua mới/xóa trong c/dtors. Ngoài ra, các hàm draw() cho các điều khiển con được gọi trong cuộc gọi draw() của cha mẹ, cho phép mọi thứ được khép kín. Đây là mã có liên quan từ một slidebar giữa cấp có chứa 3 sub nút:

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

Bây giờ chúng ta có kiểm soát khép kín mà làm thành một bối cảnh OpenGL, tất cả những gì cần thiết là giao diện từ việc kiểm soát cấp cao nhất , ví dụ như các sự kiện chuột từ 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... 
    } 
} 

Nhìn chung hệ thống này là một chút hacky, và tôi đã không được thêm vào một số tính năng như phản ứng bàn phím hoặc thay đổi kích thước, nhưng những nên dễ dàng để thêm, có thể đòi hỏi một 'loại sự kiện' trong callback (tức là chuột hoặc bàn phím). Bạn thậm chí có thể phân lớp glBase thành tiện ích trên cùng, và nó thậm chí sẽ cho phép nó nhận được parent-> childcallback. Mặc dù có rất nhiều công việc, nhưng hệ thống này chỉ yêu cầu vani C++/opengl và nó sử dụng ít tài nguyên bộ nhớ/cpu nhiều hơn so với các công cụ Qt nguyên bản, có thể bởi một hoặc hai thứ tự. Nếu bạn cần thêm thông tin, chỉ cần hỏi.

1

thay đổi kích thước và các sự kiện dễ dàng để thực hiện đặc biệt nếu bạn thực hiện một kế thừa lớp từ qglwidget đây là một đoạn video ngắn mà sẽ giúp bạn bắt đầu

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

liên quan đến bản vẽ hình ảnh và văn bản qt có qpainter có thể giúp bạn thực hiện điều đó, sử dụng QGLContext (http://qt-project.org/doc/qt-4.8/qglcontext.html) để chuyển ngữ cảnh hiện tại và hiển thị nhãn/hình ảnh/tiện ích của bạn ở dây cụ thể, nhưng đảm bảo rằng cuộc gọi chức năng này là sự kiện cuối cùng trong sự kiện sơn của bạn nó sẽ không nằm trên đầu trang

để phản ứng với sự kiện chuột khiến lớp của bạn bao gồm tiêu đề QMouseEvent, sau đó bạn có thể nhận tọa độ của con chuột trong tiện ích và chuyển chúng sang opengl bằng cách quá tải sự kiện chuột được bảo vệMoveEvent (sự kiện QMouseEvent *) {event-> pos();}

Các vấn đề liên quan