Qt3d。在三角形上绘制透明 QSphereMesh
Qt3d. Draw transparent QSphereMesh over triangles
我有一个通过 OpenGL 绘制三角形的函数
我通过按下按钮(函数 on_drawMapPushButton_clicked())绘制了两个三角形。
然后我在这些三角形上方画了一个球体。现在我明白了,球体正确绘制在第一个三角形上,但第二个三角形绘制在球体上,反之亦然。
如果我第二次按下按钮,则会在第一个和第二个三角形上正确绘制球体。
当我第三次按下按钮时,第二个三角形再次绘制在球体上。
当我第四次按下按钮时,球体会正确地绘制在第一个和第二个三角形上,依此类推。
如果我在 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
has a QNoDepthMask
渲染状态告诉它不要写入深度缓冲区。因此,不透明对象总是通过深度测试,而透明对象实际上已经绘制到该深度测试。您必须做更多的工作才能为任意场景图和相机位置正确绘制透明对象。
Qt3D 渲染图
要了解您必须执行的操作,您应该了解 Qt3D 渲染图的布局方式。如果你已经知道这一点,你可以跳过这部分。
斜体字参考下图中的项目。
如果使用Qt3DWindow
,则无法访问渲染图的根节点。它由 window 维护。您可以通过函数 activeFramegraph()
和 renderSettings()
访问框架图 的 QRenderSettings 和 根节点,您都可以调用它们在 window 上。也可以通过Qt3DWindow
的setRootEntity()
函数设置场景图的根节点。 window内部有一个QAspectEngine
,它设置了整个图的根节点,也就是上图图的渲染图的根节点 .
如果您想将一个框架图节点插入到现有的 3D 框架图 window,您必须将其添加为活动框架图的父节点,我将在下一节中进行解释。如果您在 window 到 setActiveFramegraph()
上设置了自己的自定义框架图,那么只需将其附加到末尾,这就足够了。
使用QSortPolicy
正如您已经根据其他问题发现的那样,您可以在框架图中使用 QSortPolicy
按与相机的距离对实体进行排序。你可以添加一个排序策略如下(假设view
是你的Qt3DWindow
,scene
是你的场景图的根实体,虽然我不明白为什么必须如此):
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);
此代码的问题在于此排序策略根据实体中心到相机的距离对实体进行排序。如果其中一个不透明物体比透明物体更靠近相机,它无论如何都会稍后绘制并遮挡透明物体。有关图形说明,请参见下图。
红色和黑色球体比圆环体离相机更远,这就是为什么它们先被绘制并且不遮挡圆环体。
没有红色球体的中心比环面的中心更靠近相机。它比环面渲染晚并遮挡它。
使用两个框架图分支
如果您使用两个框架图分支,则可以解决上述问题。一种绘制所有不透明实体,另一种绘制所有透明实体。为此,您必须使用 QLayer
和 QLayerFilter
。您可以将图层附加到实体,然后将图层过滤器添加到框架图中。这样您就可以排除实体进入框架图的某个分支。
假设您创建了两层,一层用于不透明对象,一层用于透明对象:
Qt3DRender::QLayer *transparentLayer = new Qt3DRender::QLayer;
Qt3DRender::QLayer *opaqueLayer = new Qt3DRender::QLayer;
您必须将透明层附加到每个透明对象,并将不透明层附加到每个不透明对象作为一个组件(使用 addComponent()
)。
不幸的是,您需要一个特殊的框架图树来包含两个相应的图层过滤器(同样,假设 view
是您的 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);
两层过滤器现在是框架图分支中的叶节点,Qt3D 将首先绘制不透明对象,然后,因为它使用相同的视口和所有内容,所以将在上方绘制透明对象。它将正确绘制它们(即不在透明对象实际位于后面的不透明对象的部分前面,因为我们没有再次清除深度缓冲区 -> 拆分帧图仅发生在相机节点上)。
现在在您的 Qt3DWindow
:
上设置新的 framegaph
view.setActiveFrameGraph(renderSurfaceSelector);
结果:
编辑 (26.03.21):正如 Patrick B. 正确指出的那样,使用建议的两层解决方案,您必须将两层作为组件添加到任何场景中的灯光。您可以通过将 QLayerFilter
上的过滤器模式设置为 QLayerFilter::FilterMode::DiscardAnyMatching
然后反转过滤器的顺序来解决这个问题。这样,transparentFilter
会丢弃任何附加了 transparentLayer
的实体 - 但不会丢弃灯光,因为它们没有 transparentLayer
。 opaqueFilter
.
反之亦然
我的错误是我创建和删除三角形和球体实体的顺序错误。
伪代码中正确顺序如下:
clearTriangles();
clearSphere();
drawTriangles();
drawSphere();
如果您将 Qt3d 与 QML 结合使用并且想要控制绘制元素的顺序,您可以通过 QML 文件中的图层顺序来控制它。
类似于:
{
objectName: "firstLayer"
id : firstLayer
}
Layer {
objectName: "secondLayer"
id: secondLayer
}
您将它们添加到图层过滤器的顺序将控制先绘制哪个:
RenderSurfaceSelector {
CameraSelector {
id : cameraSelector
camera: mainCamera
FrustumCulling {
ClearBuffers {
buffers : ClearBuffers.AllBuffers
clearColor: "#04151c"
NoDraw {}
}
LayerFilter
{
objectName: "firstLayerFilter"
id: firstLayerFilter
layers: [firstLayer]
}
LayerFilter
{
id: secondLayerFilter
objectName: "secondLayerFilter"
layers: [secondLayer]
}
然后,您添加到第二层的任何内容都将绘制在第一层之上。我用它来确保文本始终显示在形状前面,但它可以类似地用于透明胶片。
我有一个通过 OpenGL 绘制三角形的函数
我通过按下按钮(函数 on_drawMapPushButton_clicked())绘制了两个三角形。
然后我在这些三角形上方画了一个球体。现在我明白了,球体正确绘制在第一个三角形上,但第二个三角形绘制在球体上,反之亦然。
如果我第二次按下按钮,则会在第一个和第二个三角形上正确绘制球体。
当我第三次按下按钮时,第二个三角形再次绘制在球体上。
当我第四次按下按钮时,球体会正确地绘制在第一个和第二个三角形上,依此类推。
如果我在 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
has a QNoDepthMask
渲染状态告诉它不要写入深度缓冲区。因此,不透明对象总是通过深度测试,而透明对象实际上已经绘制到该深度测试。您必须做更多的工作才能为任意场景图和相机位置正确绘制透明对象。
Qt3D 渲染图
要了解您必须执行的操作,您应该了解 Qt3D 渲染图的布局方式。如果你已经知道这一点,你可以跳过这部分。
斜体字参考下图中的项目。
如果使用Qt3DWindow
,则无法访问渲染图的根节点。它由 window 维护。您可以通过函数 activeFramegraph()
和 renderSettings()
访问框架图 的 QRenderSettings 和 根节点,您都可以调用它们在 window 上。也可以通过Qt3DWindow
的setRootEntity()
函数设置场景图的根节点。 window内部有一个QAspectEngine
,它设置了整个图的根节点,也就是上图图的渲染图的根节点 .
如果您想将一个框架图节点插入到现有的 3D 框架图 window,您必须将其添加为活动框架图的父节点,我将在下一节中进行解释。如果您在 window 到 setActiveFramegraph()
上设置了自己的自定义框架图,那么只需将其附加到末尾,这就足够了。
使用QSortPolicy
正如您已经根据其他问题发现的那样,您可以在框架图中使用 QSortPolicy
按与相机的距离对实体进行排序。你可以添加一个排序策略如下(假设view
是你的Qt3DWindow
,scene
是你的场景图的根实体,虽然我不明白为什么必须如此):
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);
此代码的问题在于此排序策略根据实体中心到相机的距离对实体进行排序。如果其中一个不透明物体比透明物体更靠近相机,它无论如何都会稍后绘制并遮挡透明物体。有关图形说明,请参见下图。
红色和黑色球体比圆环体离相机更远,这就是为什么它们先被绘制并且不遮挡圆环体。
没有红色球体的中心比环面的中心更靠近相机。它比环面渲染晚并遮挡它。
使用两个框架图分支
如果您使用两个框架图分支,则可以解决上述问题。一种绘制所有不透明实体,另一种绘制所有透明实体。为此,您必须使用 QLayer
和 QLayerFilter
。您可以将图层附加到实体,然后将图层过滤器添加到框架图中。这样您就可以排除实体进入框架图的某个分支。
假设您创建了两层,一层用于不透明对象,一层用于透明对象:
Qt3DRender::QLayer *transparentLayer = new Qt3DRender::QLayer;
Qt3DRender::QLayer *opaqueLayer = new Qt3DRender::QLayer;
您必须将透明层附加到每个透明对象,并将不透明层附加到每个不透明对象作为一个组件(使用 addComponent()
)。
不幸的是,您需要一个特殊的框架图树来包含两个相应的图层过滤器(同样,假设 view
是您的 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);
两层过滤器现在是框架图分支中的叶节点,Qt3D 将首先绘制不透明对象,然后,因为它使用相同的视口和所有内容,所以将在上方绘制透明对象。它将正确绘制它们(即不在透明对象实际位于后面的不透明对象的部分前面,因为我们没有再次清除深度缓冲区 -> 拆分帧图仅发生在相机节点上)。
现在在您的 Qt3DWindow
:
view.setActiveFrameGraph(renderSurfaceSelector);
结果:
编辑 (26.03.21):正如 Patrick B. 正确指出的那样,使用建议的两层解决方案,您必须将两层作为组件添加到任何场景中的灯光。您可以通过将 QLayerFilter
上的过滤器模式设置为 QLayerFilter::FilterMode::DiscardAnyMatching
然后反转过滤器的顺序来解决这个问题。这样,transparentFilter
会丢弃任何附加了 transparentLayer
的实体 - 但不会丢弃灯光,因为它们没有 transparentLayer
。 opaqueFilter
.
我的错误是我创建和删除三角形和球体实体的顺序错误。
伪代码中正确顺序如下:
clearTriangles();
clearSphere();
drawTriangles();
drawSphere();
如果您将 Qt3d 与 QML 结合使用并且想要控制绘制元素的顺序,您可以通过 QML 文件中的图层顺序来控制它。
类似于:
{
objectName: "firstLayer"
id : firstLayer
}
Layer {
objectName: "secondLayer"
id: secondLayer
}
您将它们添加到图层过滤器的顺序将控制先绘制哪个:
RenderSurfaceSelector {
CameraSelector {
id : cameraSelector
camera: mainCamera
FrustumCulling {
ClearBuffers {
buffers : ClearBuffers.AllBuffers
clearColor: "#04151c"
NoDraw {}
}
LayerFilter
{
objectName: "firstLayerFilter"
id: firstLayerFilter
layers: [firstLayer]
}
LayerFilter
{
id: secondLayerFilter
objectName: "secondLayerFilter"
layers: [secondLayer]
}
然后,您添加到第二层的任何内容都将绘制在第一层之上。我用它来确保文本始终显示在形状前面,但它可以类似地用于透明胶片。