Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/160.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ Qt3D中QGLView(非QGLWidget)上的过度绘制和渲染后效果_C++_Opengl_Qml_Qt5_Qt3d - Fatal编程技术网

C++ Qt3D中QGLView(非QGLWidget)上的过度绘制和渲染后效果

C++ Qt3D中QGLView(非QGLWidget)上的过度绘制和渲染后效果,c++,opengl,qml,qt5,qt3d,C++,Opengl,Qml,Qt5,Qt3d,我目前正在使用Qt3D模块用C++/Qt5编写一个游戏 我可以在QGLView上渲染场景(QGLSceneNodes),但现在我仍然坚持使用一些GUI元素对场景进行过度渲染。我还没有决定是否使用QML或C++来定义界面的外观和感觉,所以我对两者都有解决的办法。(注意,QML模块称为QtQui3D,C++模块称为QT3D,它们都是QT5的一部分。 我非常喜欢基于QML的解决方案 我怎样才能多涂几层呢? 必须做到以下几点: 在给定的屏幕坐标下绘制2D项目(图像),当然支持alpha通道 使用系统字

我目前正在使用Qt3D模块用C++/Qt5编写一个游戏

我可以在QGLView上渲染场景(QGLSceneNodes),但现在我仍然坚持使用一些GUI元素对场景进行过度渲染。我还没有决定是否使用QML或C++来定义界面的外观和感觉,所以我对两者都有解决的办法。(注意,QML模块称为QtQui3D,C++模块称为QT3D,它们都是QT5的一部分。 我非常喜欢基于QML的解决方案

我怎样才能多涂几层呢?

必须做到以下几点:

  • 在给定的屏幕坐标下绘制2D项目(图像),当然支持alpha通道
  • 使用系统字体绘制文本,可能首先在图像上绘制,并将其用作Qt3D OpenGL上下文中的纹理
  • 在屏幕坐标中对鼠标输入作出反应(与3D场景对象拾取相反)
我认为在2D GUI部分使用另一个QGLSCeneode是完全可能的。但是我认为在渲染过程中(使用顶点着色器)定位和定向某些场景节点以重新定向和定位是没有意义的,会引入数值错误,而且效率似乎很低

使用另一个“GUI”顶点着色器是否正确

(如何)使后期渲染效果和过度渲染协同工作?

如果我可以执行以下渲染后效果,那就太好了:

  • 阅读先前渲染的场景,应用一些2D图像效果(我可以想象将它们作为片段着色器实现,并使用此效果着色器在最终屏幕上渲染先前渲染的缓冲区)。这使我能够使GUI的某些部分看起来像带有模糊效果的玻璃
  • 一些更高级的效果可以使场景看起来更逼真:深度和运动模糊、依赖于热量的空气闪烁、辉光效果。它们都可以在渲染期间使用片段着色器(这不应该是问题的一部分)和附加缓冲区来描述。它们应该放在主屏幕渲染过程和GUI过度渲染之间,这就是为什么我在这个问题中包括它

当我使用一个特殊的着色器程序实现过度绘制时,我发现了一个问题:我无法访问GUI后面的像素来应用高斯模糊等效果,使GUI看起来像玻璃我必须将场景渲染到另一个缓冲区,而不是QGLView上。这是我的主要问题…

我在通过qglwidget制作opengl游戏时也遇到过类似问题,我提出的解决方案是使用正交投影来渲染scratch构建的控件。在主绘图函数的末尾,我调用了一个单独的函数来设置视图,然后绘制2d位。对于鼠标事件,我捕获发送到主窗口小部件的事件,然后使用命中测试、递归坐标偏移和伪回调的组合来嵌套控件。在我的实现中,我一次一个地将命令转发给顶级控件(主要是滚动框),但是如果需要动态添加控件,您可以轻松添加链表结构。我目前只使用四边形将零件渲染为彩色面板,但添加纹理甚至使其成为响应动态照明的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++;
}
现在来看看控件本身。我的所有控件(如按钮、滑块、标签等)都来自一个公共基类,该基类具有空的虚拟函数,允许它们以不可知的方式与其他类型交互。基底有一些典型的东西,比如高度、坐标等,还有一些指针来处理回调和文本呈现。还包括通用的hittest(x,y)函数,该函数告诉您是否已单击它。如果需要椭圆控件,可以将其设置为虚拟控件。下面是一个简单按钮的基类、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);
  }
}
现在我们有了能够呈现到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子类化为最顶层的小部件,它甚至允许它接收parent->childcallback。尽管工作量很大,但这个系统只需要普通的c++/opengl,而且它使用的cpu/内存资源比原生的Qt资源少得多,可能只有一两个数量级。如果您需要更多信息,只需询问。

如果您让一个类从qglwidget继承,则调整大小和事件很容易实现 下面是一段简短的视频,可以帮助您开始学习

关于绘制图像和文本,qt有qpainter可以帮助您完成这项工作, 使用QGLContext()将当前上下文传递给它,并在特定坐标系下呈现标签/图像/小部件,但请确保此函数调用是绘制事件中的最后一个函数调用,否则它不会位于顶部


要对鼠标事件做出反应,请使您的类包含QMouseEvent头,然后您可以通过重载受保护的事件mouseMoveEvent(QMouseEvent*event){event->pos();}

将渲染模式设置为正交模式而不是透视模式,从而获取小部件中鼠标的坐标并将其传递给opengl。这使得GUI着色器变得不必要。但是,我仍然认为必须有一种内置的方法来在Qt3D中进行过度绘制(因为在好的旧QGLWidget中有一种方法(我不想使用它,因为它在Qt5小部件模块中,不能使用QGLPainter进行渲染))。这似乎是一种可能性(听起来很不错)
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);
  }
}
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);
  }
}
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...
  }
}