I draw some 2d text entities in a Qt3D QML scene but some of the texts always render opaque, i.e hide the contents behind them. When looking at the scene from behind ( changing the position of the camera to Qt.vector3d(0,0,-40)
) all texts render OK.
The following image shows the wrong behaviour, I would expect the text "AAARGH" not to be rendered on a white background, but the green text shining through.
Platform is Windows 64-bit, Qt5.13.0, and Visual Studio 2019.
See the following small example that demonstrates the issue:
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();
}
I suppose the reason of this behaviour comes from the GLSL shaders (distancefieldtext.vert
and distancefieldtext.frag
) that are used in Qt3D to render the Text2dEntity.
See attached shader sources.
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;
}
Any ideas on this how to make Qt3D render the text2DEntities with the text itself opaque and the spaces between the text transparant, independent on the viewing direction? Thanks in advance.
Edit
I must have inadvertently changed something in the example, as changing the position of the camera does not show the expected behaviour anymore. I will correct that on Monday when I have access to my work environment.
Update
As I needed double sided lighting for my entities, I had an additional CullFace
component with NoCulling
added to the RenderStateSet
, that explains this behaviour. My FrameGraph was looking like this:
components: [
RenderSettings {
activeFrameGraph: RenderStateSet {
renderStates: [
CullFace { mode: CullFace.NoCulling }
]
ForwardRenderer {
camera: camera
clearColor: "transparent"
}
}
},
InputSettings { }
]
When viewing from the back side, as the entities were defined from back to front the rendering was correct. This is stated explicitly in the documentation of SortPolicy:
"If QSortPolicy is not present in the FrameGraph, entities are drawn in the order they appear in the entity hierarchy."
When adding an additional SortPolicy
component with BackToFront
to the FrameGraph the rendering was correct independent of the viewing direction. The FrameGraph then looked like this:
components: [
RenderSettings {
activeFrameGraph: SortPolicy {
sortTypes: [ SortPolicy.BackToFront ]
RenderStateSet {
renderStates: [
CullFace { mode: CullFace.NoCulling }
]
ForwardRenderer {
camera: camera
clearColor: "transparent"
}
}
}
},
InputSettings { }
]