Text2DEntity 呈现不透明并隐藏其背后的其他实体
Text2DEntity renders opaque and hides other entities behind it
我在 Qt3D QML 场景中绘制了一些 2d 文本实体,但一些文本总是呈现 不透明,即隐藏它们后面的内容。从后面看场景时(将相机的位置更改为 Qt.vector3d(0,0,-40)
)所有文本都渲染正常。
下图显示了错误的行为,我希望文本 "AAARGH" 不会呈现在白色背景上,而是绿色的文本闪耀。
平台是 Windows 64 位、Qt5.13.0 和 Visual Studio 2019。
请参阅以下演示该问题的小示例:
BrokenEntity.qml
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import Qt3D.Extras 2.13
import QtQuick 2.0 as QQ2
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 0.0, 40 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController { camera: camera }
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera
clearColor: "transparent"
}
},
InputSettings { }
]
Entity {
components: [ Transform { translation: Qt.vector3d(-12.5,-5,-20) } ]
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 5
color: Qt.rgba(0, 0, 1, 0.5)
text: "AAARGH"
width: text.length * font.pointSize
height: font.pointSize * 1.2
}
}
Entity {
PhongMaterial {
id: material
ambient: Qt.rgba(1, 1, 0, 1)
diffuse: Qt.rgba(1, 1, 0, 1)
}
SphereMesh {
id: sphereMesh
radius: 1
rings: 50
slices: 50
}
Transform {
id: sphereTransform
translation: Qt.vector3d(0,0,-25)
scale3D: Qt.vector3d(1, 1, 1)
}
components: [ sphereMesh, material, sphereTransform ]
}
Entity {
components: [ Transform { translation: Qt.vector3d(-25,-5,-30) } ]
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 10
color: Qt.rgba(0, 1, 0, 1.0)
text: "BBBRGH"
width: text.length * font.pointSize
height: font.pointSize * 1.2
}
}
}
main.qml
import QtQuick 2.0
import QtQuick.Scene3D 2.0
Item {
Rectangle {
id: scene
anchors.fill: parent
anchors.margins: 50
color: "white"
Scene3D {
id: scene3d
anchors.fill: parent
anchors.margins: 10
focus: true
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
BrokenEntity {}
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQuickView>
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QQuickView view;
view.resize(500, 500);
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:/main.qml"));
view.show();
return app.exec();
}
我想这种行为的原因来自 Qt3D 中用于呈现 Text2dEntity 的 GLSL 着色器(distancefieldtext.vert
和 distancefieldtext.frag
)。
查看附加的着色器源代码。
distancefieldtext.vert
#version 150 core
in vec3 vertexPosition;
in vec2 vertexTexCoord;
out vec2 texCoord;
out float zValue;
uniform mat4 modelView;
uniform mat4 mvp;
void main()
{
texCoord = vertexTexCoord;
zValue = vertexPosition.z;
gl_Position = mvp * vec4(vertexPosition.xy, 0.0, 1.0);
}
distancefieldtext.frag
#version 150 core
uniform sampler2D distanceFieldTexture;
uniform float minAlpha;
uniform float maxAlpha;
uniform float textureSize;
uniform vec4 color;
in vec2 texCoord;
in float zValue;
out vec4 fragColor;
void main()
{
// determine the scale of the glyph texture within pixel-space coordinates
// (that is, how many pixels are drawn for each texel)
vec2 texelDeltaX = abs(dFdx(texCoord));
vec2 texelDeltaY = abs(dFdy(texCoord));
float avgTexelDelta = textureSize * 0.5 * (texelDeltaX.x + texelDeltaX.y + texelDeltaY.x + texelDeltaY.y);
float texScale = 1.0 / avgTexelDelta;
// scaled to interval [0.0, 0.15]
float devScaleMin = 0.00;
float devScaleMax = 0.15;
float scaled = (clamp(texScale, devScaleMin, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin);
// thickness of glyphs should increase a lot for very small glyphs to make them readable
float base = 0.5;
float threshold = base * scaled;
float range = 0.06 / texScale;
float minAlpha = threshold - range;
float maxAlpha = threshold + range;
float distVal = texture(distanceFieldTexture, texCoord).r;
fragColor = color * smoothstep(minAlpha, maxAlpha, distVal);
gl_FragDepth = gl_FragCoord.z - zValue * 0.00001;
}
关于如何让 Qt3D 渲染 text2DEntities 的任何想法,文本本身不透明,文本之间的空间透明,独立于观察方向?提前致谢。
编辑
我一定是无意中更改了示例中的某些内容,因为更改相机的位置不再显示预期的行为。我将在周一访问我的工作环境时更正该错误。
更新
因为我的实体需要双面照明,所以我有一个额外的 CullFace
组件,其中 NoCulling
添加到 RenderStateSet
,这解释了这种行为。我的 FrameGraph 看起来像这样:
components: [
RenderSettings {
activeFrameGraph: RenderStateSet {
renderStates: [
CullFace { mode: CullFace.NoCulling }
]
ForwardRenderer {
camera: camera
clearColor: "transparent"
}
}
},
InputSettings { }
]
从背面查看时,由于实体是从后到前定义的,因此渲染是正确的。 SortPolicy 的文档中明确说明了这一点:
"If QSortPolicy is not present in the FrameGraph, entities are drawn
in the order they appear in the entity hierarchy."
当向 FrameGraph 添加一个带有 BackToFront
的附加 SortPolicy
组件时,无论观察方向如何,渲染都是正确的。 FrameGraph 看起来像这样:
components: [
RenderSettings {
activeFrameGraph: SortPolicy {
sortTypes: [ SortPolicy.BackToFront ]
RenderStateSet {
renderStates: [
CullFace { mode: CullFace.NoCulling }
]
ForwardRenderer {
camera: camera
clearColor: "transparent"
}
}
}
},
InputSettings { }
]
问题似乎是行
zValue = vertexPosition.z;
在顶点着色器中。 vertexPosition
是模型 space 中的坐标。如果你想计算到相机的 z 距离,那么你必须通过 modelView
矩阵将坐标转换为视图 space:
vec4 viewPosition = modelView * vec4(vertexPosition.xyz, 1.0);
zValue = -viewPosition.z;
注意,由于视图 space z 轴指向视口外,因此必须反转坐标才能获得到相机的距离。
另一种对我来说似乎更正确的可能性是计算剪辑 space 坐标并进一步标准化设备坐标。剪辑space坐标由模型视图矩阵(mvp
)和归一化设备坐标变换计算得到,由Perspective divide:
vec4 clipPosition = modelView * vec4(vertexPosition.xyz, 1.0);
zValue = clipPosition.z / clipPosition.w;
规范化的设备坐标在 [-1, 1] 范围内。 z 分量可以线性映射到片段的深度。默认情况下,深度范围为 [0, 1](除了由 glDepthRange
.
更改
所以在片段着色器中gl_FragDepth
可以设置为:
gl_FragDepth = zValue * 0.5 + 0.5;
如果您使用 alpha Blending, then the Depth Test,则必须禁用。
其他对象后面的对象可能根本不会被绘制,因为它们被深度测试丢弃了。
当然这取决于绘图顺序。如果首先绘制被覆盖的文本,那么它将起作用。但如果它是最后绘制的,那么它就会被丢弃。这解释了取决于观察方向的不同行为。
请注意,当混合处于活动状态时,颜色不会影响颜色缓冲区(如果 alpha 为 0),但如果启用深度测试,那么深度当然会写入深度缓冲区。
唯一的选择是按照从后到前的排序顺序绘制对象。当然,顺序取决于观察方向,因此必须按帧对对象进行排序。
我做了一些测试,为了得到不需要的行为,我通过打乱所用实体的顺序对您的代码进行了一些更改。如您所知,实体的顺序很重要。
在我的示例中,text2DEntity "textFront" 在层次结构中放在 text2DEntity "textBack" 之前。所以在不改变你的渲染环境的情况下,我们得到这样的东西:
我添加了一个红色球体来测试更深一点。
我找到了一个使用前向渲染器而不使用深度缓冲区的解决方案。这是结果(当然,我没有改变实体的顺序):
我们必须使用 SortPolicy,以便前向渲染器知道哪个对象在另一个对象前面。这将改变实体相对于相机距离的顺序,而不是 qml 文件的层次结构顺序。
components: [
RenderSettings {
activeFrameGraph: SortPolicy {
sortTypes: [
SortPolicy.BackToFront
]
ForwardRenderer {
camera: camera
clearColor: "black"
}
}
},
// Event Source will be set by the Qt3DQuickWindow
InputSettings { }
]
为了便于测试,这里是完整的文件内容:
BrokenEntity.qml
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Input 2.12
import Qt3D.Extras 2.13
import QtQuick 2.12 as QQ2
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 0.0, 40 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController { camera: camera }
components: [
RenderSettings {
activeFrameGraph: SortPolicy {
sortTypes: [
SortPolicy.BackToFront
]
ForwardRenderer {
camera: camera
clearColor: "black"
}
}
},
// Event Source will be set by the Qt3DQuickWindow
InputSettings { }
]
Entity {
id: textFront
components: [ Transform { translation: Qt.vector3d(-12.5,-5,-20) } ] //IKKE
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 3
color: "white"
text: "textFront"
width: text.length * font.pointSize*2
height: font.pointSize * 4
}
}
PhongMaterial {
id: material
ambient: Qt.rgba(1, 1, 0, 1)
diffuse: Qt.rgba(1, 1, 0, 1)
}
PhongMaterial {
id: material2
ambient: Qt.rgba(1, 0, 0, 1)
diffuse: Qt.rgba(1, 0, 0, 1)
}
SphereMesh {
id: sphereMesh
radius: 5
rings: 50
slices: 50
}
Entity {
id: mysphere
Transform {
id: sphereTransform
translation: Qt.vector3d(0,0,-25)
scale3D: Qt.vector3d(1, 1, 1)
}
components: [ sphereMesh, material, sphereTransform ]
}
Entity {
id: mysphere2
Transform {
id: sphereTransform2
translation: Qt.vector3d(0,0,-50)
scale3D: Qt.vector3d(1, 1, 1)
}
components: [ sphereMesh, material2, sphereTransform2 ]
}
Entity {
id: textBack
components: [ Transform { translation: Qt.vector3d(-25,-5,-30) } ]
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 10
color: Qt.rgba(0, 1, 0, 1.0)
text: "textBack"
width: text.length * font.pointSize
height: font.pointSize * 2
}
}
}
我在 Qt3D QML 场景中绘制了一些 2d 文本实体,但一些文本总是呈现 不透明,即隐藏它们后面的内容。从后面看场景时(将相机的位置更改为 Qt.vector3d(0,0,-40)
)所有文本都渲染正常。
下图显示了错误的行为,我希望文本 "AAARGH" 不会呈现在白色背景上,而是绿色的文本闪耀。
平台是 Windows 64 位、Qt5.13.0 和 Visual Studio 2019。
请参阅以下演示该问题的小示例:
BrokenEntity.qml
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import Qt3D.Extras 2.13
import QtQuick 2.0 as QQ2
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 0.0, 40 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController { camera: camera }
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera
clearColor: "transparent"
}
},
InputSettings { }
]
Entity {
components: [ Transform { translation: Qt.vector3d(-12.5,-5,-20) } ]
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 5
color: Qt.rgba(0, 0, 1, 0.5)
text: "AAARGH"
width: text.length * font.pointSize
height: font.pointSize * 1.2
}
}
Entity {
PhongMaterial {
id: material
ambient: Qt.rgba(1, 1, 0, 1)
diffuse: Qt.rgba(1, 1, 0, 1)
}
SphereMesh {
id: sphereMesh
radius: 1
rings: 50
slices: 50
}
Transform {
id: sphereTransform
translation: Qt.vector3d(0,0,-25)
scale3D: Qt.vector3d(1, 1, 1)
}
components: [ sphereMesh, material, sphereTransform ]
}
Entity {
components: [ Transform { translation: Qt.vector3d(-25,-5,-30) } ]
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 10
color: Qt.rgba(0, 1, 0, 1.0)
text: "BBBRGH"
width: text.length * font.pointSize
height: font.pointSize * 1.2
}
}
}
main.qml
import QtQuick 2.0
import QtQuick.Scene3D 2.0
Item {
Rectangle {
id: scene
anchors.fill: parent
anchors.margins: 50
color: "white"
Scene3D {
id: scene3d
anchors.fill: parent
anchors.margins: 10
focus: true
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
BrokenEntity {}
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQuickView>
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QQuickView view;
view.resize(500, 500);
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:/main.qml"));
view.show();
return app.exec();
}
我想这种行为的原因来自 Qt3D 中用于呈现 Text2dEntity 的 GLSL 着色器(distancefieldtext.vert
和 distancefieldtext.frag
)。
查看附加的着色器源代码。
distancefieldtext.vert
#version 150 core
in vec3 vertexPosition;
in vec2 vertexTexCoord;
out vec2 texCoord;
out float zValue;
uniform mat4 modelView;
uniform mat4 mvp;
void main()
{
texCoord = vertexTexCoord;
zValue = vertexPosition.z;
gl_Position = mvp * vec4(vertexPosition.xy, 0.0, 1.0);
}
distancefieldtext.frag
#version 150 core
uniform sampler2D distanceFieldTexture;
uniform float minAlpha;
uniform float maxAlpha;
uniform float textureSize;
uniform vec4 color;
in vec2 texCoord;
in float zValue;
out vec4 fragColor;
void main()
{
// determine the scale of the glyph texture within pixel-space coordinates
// (that is, how many pixels are drawn for each texel)
vec2 texelDeltaX = abs(dFdx(texCoord));
vec2 texelDeltaY = abs(dFdy(texCoord));
float avgTexelDelta = textureSize * 0.5 * (texelDeltaX.x + texelDeltaX.y + texelDeltaY.x + texelDeltaY.y);
float texScale = 1.0 / avgTexelDelta;
// scaled to interval [0.0, 0.15]
float devScaleMin = 0.00;
float devScaleMax = 0.15;
float scaled = (clamp(texScale, devScaleMin, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin);
// thickness of glyphs should increase a lot for very small glyphs to make them readable
float base = 0.5;
float threshold = base * scaled;
float range = 0.06 / texScale;
float minAlpha = threshold - range;
float maxAlpha = threshold + range;
float distVal = texture(distanceFieldTexture, texCoord).r;
fragColor = color * smoothstep(minAlpha, maxAlpha, distVal);
gl_FragDepth = gl_FragCoord.z - zValue * 0.00001;
}
关于如何让 Qt3D 渲染 text2DEntities 的任何想法,文本本身不透明,文本之间的空间透明,独立于观察方向?提前致谢。
编辑
我一定是无意中更改了示例中的某些内容,因为更改相机的位置不再显示预期的行为。我将在周一访问我的工作环境时更正该错误。
更新
因为我的实体需要双面照明,所以我有一个额外的 CullFace
组件,其中 NoCulling
添加到 RenderStateSet
,这解释了这种行为。我的 FrameGraph 看起来像这样:
components: [
RenderSettings {
activeFrameGraph: RenderStateSet {
renderStates: [
CullFace { mode: CullFace.NoCulling }
]
ForwardRenderer {
camera: camera
clearColor: "transparent"
}
}
},
InputSettings { }
]
从背面查看时,由于实体是从后到前定义的,因此渲染是正确的。 SortPolicy 的文档中明确说明了这一点:
"If QSortPolicy is not present in the FrameGraph, entities are drawn in the order they appear in the entity hierarchy."
当向 FrameGraph 添加一个带有 BackToFront
的附加 SortPolicy
组件时,无论观察方向如何,渲染都是正确的。 FrameGraph 看起来像这样:
components: [
RenderSettings {
activeFrameGraph: SortPolicy {
sortTypes: [ SortPolicy.BackToFront ]
RenderStateSet {
renderStates: [
CullFace { mode: CullFace.NoCulling }
]
ForwardRenderer {
camera: camera
clearColor: "transparent"
}
}
}
},
InputSettings { }
]
问题似乎是行
zValue = vertexPosition.z;
在顶点着色器中。 vertexPosition
是模型 space 中的坐标。如果你想计算到相机的 z 距离,那么你必须通过 modelView
矩阵将坐标转换为视图 space:
vec4 viewPosition = modelView * vec4(vertexPosition.xyz, 1.0);
zValue = -viewPosition.z;
注意,由于视图 space z 轴指向视口外,因此必须反转坐标才能获得到相机的距离。
另一种对我来说似乎更正确的可能性是计算剪辑 space 坐标并进一步标准化设备坐标。剪辑space坐标由模型视图矩阵(mvp
)和归一化设备坐标变换计算得到,由Perspective divide:
vec4 clipPosition = modelView * vec4(vertexPosition.xyz, 1.0);
zValue = clipPosition.z / clipPosition.w;
规范化的设备坐标在 [-1, 1] 范围内。 z 分量可以线性映射到片段的深度。默认情况下,深度范围为 [0, 1](除了由 glDepthRange
.
更改
所以在片段着色器中gl_FragDepth
可以设置为:
gl_FragDepth = zValue * 0.5 + 0.5;
如果您使用 alpha Blending, then the Depth Test,则必须禁用。
其他对象后面的对象可能根本不会被绘制,因为它们被深度测试丢弃了。
当然这取决于绘图顺序。如果首先绘制被覆盖的文本,那么它将起作用。但如果它是最后绘制的,那么它就会被丢弃。这解释了取决于观察方向的不同行为。
请注意,当混合处于活动状态时,颜色不会影响颜色缓冲区(如果 alpha 为 0),但如果启用深度测试,那么深度当然会写入深度缓冲区。
唯一的选择是按照从后到前的排序顺序绘制对象。当然,顺序取决于观察方向,因此必须按帧对对象进行排序。
我做了一些测试,为了得到不需要的行为,我通过打乱所用实体的顺序对您的代码进行了一些更改。如您所知,实体的顺序很重要。
在我的示例中,text2DEntity "textFront" 在层次结构中放在 text2DEntity "textBack" 之前。所以在不改变你的渲染环境的情况下,我们得到这样的东西:
我找到了一个使用前向渲染器而不使用深度缓冲区的解决方案。这是结果(当然,我没有改变实体的顺序):
我们必须使用 SortPolicy,以便前向渲染器知道哪个对象在另一个对象前面。这将改变实体相对于相机距离的顺序,而不是 qml 文件的层次结构顺序。
components: [
RenderSettings {
activeFrameGraph: SortPolicy {
sortTypes: [
SortPolicy.BackToFront
]
ForwardRenderer {
camera: camera
clearColor: "black"
}
}
},
// Event Source will be set by the Qt3DQuickWindow
InputSettings { }
]
为了便于测试,这里是完整的文件内容:
BrokenEntity.qml
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Input 2.12
import Qt3D.Extras 2.13
import QtQuick 2.12 as QQ2
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 0.0, 40 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController { camera: camera }
components: [
RenderSettings {
activeFrameGraph: SortPolicy {
sortTypes: [
SortPolicy.BackToFront
]
ForwardRenderer {
camera: camera
clearColor: "black"
}
}
},
// Event Source will be set by the Qt3DQuickWindow
InputSettings { }
]
Entity {
id: textFront
components: [ Transform { translation: Qt.vector3d(-12.5,-5,-20) } ] //IKKE
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 3
color: "white"
text: "textFront"
width: text.length * font.pointSize*2
height: font.pointSize * 4
}
}
PhongMaterial {
id: material
ambient: Qt.rgba(1, 1, 0, 1)
diffuse: Qt.rgba(1, 1, 0, 1)
}
PhongMaterial {
id: material2
ambient: Qt.rgba(1, 0, 0, 1)
diffuse: Qt.rgba(1, 0, 0, 1)
}
SphereMesh {
id: sphereMesh
radius: 5
rings: 50
slices: 50
}
Entity {
id: mysphere
Transform {
id: sphereTransform
translation: Qt.vector3d(0,0,-25)
scale3D: Qt.vector3d(1, 1, 1)
}
components: [ sphereMesh, material, sphereTransform ]
}
Entity {
id: mysphere2
Transform {
id: sphereTransform2
translation: Qt.vector3d(0,0,-50)
scale3D: Qt.vector3d(1, 1, 1)
}
components: [ sphereMesh, material2, sphereTransform2 ]
}
Entity {
id: textBack
components: [ Transform { translation: Qt.vector3d(-25,-5,-30) } ]
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 10
color: Qt.rgba(0, 1, 0, 1.0)
text: "textBack"
width: text.length * font.pointSize
height: font.pointSize * 2
}
}
}