c – Qt3d.在三角形上绘制透明的QSphereMesh
作者:互联网
我有一个通过OpenGL绘制三角形的函数
我通过按下按钮绘制两个三角形(函数on_drawMapPushButton_clicked()).
然后我绘制一个放在这些三角形上方的球体.现在我看到,该球体在第一个三角形上正确绘制,但是第二个三角形在球体上绘制而不是相反.
如果我第二次按下按钮,则会在第一个和第二个三角形上正确绘制spehere.
当我第三次按下按钮时,第二个三角形再次划过球体.
当我第四次按下按钮时,在第一和第二个三角形上正确绘制了spehere,依此类推.
如果我在sphereMesh QPhongMaterial而不是QPhongAlphaMaterial中使用,则始终在第一个和第二个三角形上正确绘制spehere.喜欢它必须是.
我无法理解我做错了什么让我的球体总是在三角形上绘制.
代码,绘制透明球体:
selectModel_ = new Qt3DExtras::QSphereMesh(selectEntity_);
selectModel_->setRadius(75);
selectModel_->setSlices(150);
selectMaterial_ = new Qt3DExtras::QPhongAlphaMaterial(selectEntity_);
selectMaterial_->setAmbient(QColor(28, 61, 136));
selectMaterial_->setDiffuse(QColor(11, 56, 159));
selectMaterial_->setSpecular(QColor(10, 67, 199));
selectMaterial_->setShininess(0.8f);
selectEntity_->addComponent(selectModel_);
selectEntity_->addComponent(selectMaterial_);
函数drawTriangles:
void drawTriangles(QPolygonF triangles, QColor color){
int numOfVertices = triangles.size();
// Create and fill vertex buffer
QByteArray bufferBytes;
bufferBytes.resize(3 * numOfVertices * static_cast<int>(sizeof(float)));
float *positions = reinterpret_cast<float*>(bufferBytes.data());
for(auto point : triangles){
*positions++ = static_cast<float>(point.x());
*positions++ = 0.0f; //We need to drow only on the surface
*positions++ = static_cast<float>(point.y());
}
geometry_ = new Qt3DRender::QGeometry(mapEntity_);
auto *buf = new Qt3DRender::QBuffer(geometry_);
buf->setData(bufferBytes);
positionAttribute_ = new Qt3DRender::QAttribute(mapEntity_);
positionAttribute_->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
positionAttribute_->setVertexBaseType(Qt3DRender::QAttribute::Float); //In our buffer we will have only floats
positionAttribute_->setVertexSize(3); // Size of a vertex
positionAttribute_->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); // Attribute type
positionAttribute_->setByteStride(3 * sizeof(float));
positionAttribute_->setBuffer(buf);
geometry_->addAttribute(positionAttribute_); // Add attribute to ours Qt3DRender::QGeometry
// Create and fill an index buffer
QByteArray indexBytes;
indexBytes.resize(numOfVertices * static_cast<int>(sizeof(unsigned int))); // start to end
unsigned int *indices = reinterpret_cast<unsigned int*>(indexBytes.data());
for(unsigned int i = 0; i < static_cast<unsigned int>(numOfVertices); ++i) {
*indices++ = i;
}
auto *indexBuffer = new Qt3DRender::QBuffer(geometry_);
indexBuffer->setData(indexBytes);
indexAttribute_ = new Qt3DRender::QAttribute(geometry_);
indexAttribute_->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); //In our buffer we will have only unsigned ints
indexAttribute_->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); // Attribute type
indexAttribute_->setBuffer(indexBuffer);
indexAttribute_->setCount(static_cast<unsigned int>(numOfVertices)); // Set count of our vertices
geometry_->addAttribute(indexAttribute_); // Add the attribute to ours Qt3DRender::QGeometry
shape_ = new Qt3DRender::QGeometryRenderer(mapEntity_);
shape_->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
shape_->setGeometry(geometry_);
//Create material
material_ = new Qt3DExtras::QPhongMaterial(mapEntity_);
material_->setAmbient(color);
trianglesEntity_ = new Qt3DCore::QEntity(mapEntity_);
trianglesEntity_->addComponent(shape_);
trianglesEntity_->addComponent(material_);
}
按下按钮处理程序on_drawMapPushButton_clicked():
void on_drawMapPushButton_clicked()
{
clearMap(); //Implementation is above
QPolygonF triangle1;
triangle1 << QPointF( 0 ,-1000) << QPointF(0 ,1000) << QPointF(1000, -1000);
drawTriangles(triangle1, Qt::black);
QPolygonF triangle2;
triangle2 << QPointF(-1000,-1000) << QPointF(-100,1000) << QPointF(-100,-1000);
drawTriangles(triangle2, Qt::red);
}
地图清除功能clearMap():
void clearMap()
{
if(mapEntity_){
delete mapEntity_;
mapEntity_ = nullptr;
mapEntity_ = new Qt3DCore::QEntity(view3dRootEntity_);
}
}
解决方法:
好的,这是延伸答案.
这有时会发生的原因有时并不取决于您的实体的顺序.如果您尝试使用两个简单的球体,一个是透明球体,另一个是非球体,您会看到当稍后添加透明球体时,它将被绘制在不透明物体上方 – 就像您想要的那样.
发生这种情况是因为首先绘制不透明对象(它首先出现在场景图中),然后是透明对象,它将为您提供所需的结果.在首先绘制透明对象的另一种情况下,上面绘制了不透明对象,因为QPhongAlphaMaterial
具有QNoDepthMask
渲染状态,该状态告诉它不要写入深度缓冲区.因此,不透明对象总是通过深度测试,透明对象实际上已经绘制到深度测试.您必须做更多工作才能为任意场景图和摄像机位置正确绘制透明对象.
Qt3D渲染图
要了解您必须做什么,您应该了解Qt3D渲染图的布局方式.如果你已经知道这一点,你可以跳过这一部分.
斜体字在下图中引用图形图像中的项目.
如果使用Qt3DWindow,则无法访问渲染图的根节点.它由窗口维护.您可以通过函数activeFramegraph()和renderSettings()访问框架图的QRenderSettings和根节点,您可以在窗口上调用它们.您还可以通过Qt3DWindow的setRootEntity()函数设置场景图的根节点.窗口内部有一个QAspectEngine
,它设置整个图的根节点,这是上图图中渲染图的根节点.
如果要将framegraph节点插入到3D窗口的现有框架图中,则必须将其添加为活动框架图的父级,我将在下一节中介绍.如果您有通过setActiveFramegraph()在窗口上设置的自定义框架图,那么只需将其附加到最后,这应该足够了.
使用QSortPolicy
正如您已经根据其他问题找到的那样,您可以在框架图中使用QSortPolicy来按照距相机的距离对实体进行排序.您可以按如下方式添加排序策略(假设视图是您的Qt3DWindow,而场景是场景图的根实体,尽管我不明白为什么必须这样):
Qt3DRender::QFrameGraphNode *framegraph = view.activeFrameGraph();
Qt3DRender::QSortPolicy *sortPolicy = new Qt3DRender::QSortPolicy(scene);
framegraph->setParent(sortPolicy);
QVector<Qt3DRender::QSortPolicy::SortType> sortTypes =
QVector<Qt3DRender::QSortPolicy::SortType>() << Qt3DRender::QSortPolicy::BackToFront;
sortPolicy->setSortTypes(sortTypes);
view.setActiveFrameGraph(framegraph);
此代码的问题在于此排序策略按实体中心与摄像机的距离对实体进行排序.如果其中一个不透明物体比透明物体更靠近相机,则它随后会被绘制并遮挡透明物体.有关图形说明,请参见下图.
红色和黑色球体比环面更远离相机,这就是为什么它们首先被吸引而且它们不会遮挡圆环.
没有红色球体的中心比环面的中心更靠近相机.它比圆环更晚渲染并将其遮挡.
使用两个Framegraph分支
如果使用两个framegraph分支,则可以解决上述问题.绘制所有不透明实体的实体和绘制所有透明实体的实体.要实现这一点,您必须使用QLayer和QLayerFilter.您可以将图层附加到实体,然后将图层过滤器添加到框架图.这样,您可以排除实体进入框架图的某个分支.
假设您创建了两个图层,一个用于不透明对象,另一个用于透明对象:
Qt3DRender::QLayer *transparentLayer = new Qt3DRender::QLayer;
Qt3DRender::QLayer *opaqueLayer = new Qt3DRender::QLayer;
您必须将透明图层附加到每个透明对象,并将不透明图层作为组件附加到每个不透明对象(使用addComponent()).
不幸的是,你需要一个特殊的framegraph树来包含两个相应的图层过滤器(同样,假设视图是你的Qt3DWindow):
Qt3DRender::QRenderSurfaceSelector *renderSurfaceSelector
= new Qt3DRender::QRenderSurfaceSelector();
renderSurfaceSelector->setSurface(&view);
Qt3DRender::QClearBuffers *clearBuffers
= new Qt3DRender::QClearBuffers(renderSurfaceSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::AllBuffers);
clearBuffers->setClearColor(Qt::white);
这是清除缓冲区的第一个分支.现在添加以下代码:
Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport(renderSurfaceSelector);
Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(viewport);
Qt3DRender::QCamera *camera = new Qt3DRender::QCamera(cameraSelector);
// set your camera parameters here
cameraSelector->setCamera(camera);
由于您将QViewport创建为QRenderSurfaceSelector的子级,因此它现在是框架图中与QClearBuffers相关的兄弟.您可以看到示例框架图here的图示.
现在,您必须创建包含图层过滤器的两个叶节点. Qt3D引擎在到达叶子时总是执行整个分支.这意味着首先绘制不透明对象,然后绘制透明对象.
// not entirely sure why transparent filter has to go first
// I would have expected the reversed order of the filters but this works...
Qt3DRender::QLayerFilter *transparentFilter = new Qt3DRender::QLayerFilter(camera);
transparentFilter->addLayer(transparentLayer);
Qt3DRender::QLayerFilter *opaqueFilter = new Qt3DRender::QLayerFilter(camera);
opaqueFilter->addLayer(opaqueLayer);
两层滤镜现在是framegraph分支中的叶节点,Qt3D将首先绘制不透明对象,然后,由于它使用相同的视口和所有内容,将绘制上面的透明对象.它将正确绘制它们(即不在透明对象实际位于后面的不透明对象的部分前面,因为我们没有再次清除深度缓冲区 – >分割帧图仅在相机节点上发生).
现在在Qt3DWindow上设置新的framegaph:
view.setActiveFrameGraph(renderSurfaceSelector);
结果:
标签:c,opengl,qt,qt3d 来源: https://codeday.me/bug/20190929/1832944.html