Text2DEntity renders opaque and hides other entities behind it

一个人想着一个人 提交于 2020-01-02 07:19:30

问题


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 { }
]

回答1:


The issue seems to be the line

zValue = vertexPosition.z;

in the vertex shader. vertexPosition is a coordinate in model space. If you want to calculate the the z distance to the camera then you've to transform the coordinate to view space by the modelView matrix:

vec4 viewPosition = modelView * vec4(vertexPosition.xyz, 1.0);
zValue = -viewPosition.z;

Note, since the view space z axis points out of the viewport, the coordinate has to be inverted to get the distance to the camera.


Another possibility, which seems even be more correct to me, is to calculate the clip space coordinate and further the normalized device coordinate. The clip space coordinate is calculated by the transformation by the model view matrix (mvp) and the normalized device coordinate, by Perspective divide:

vec4 clipPosition = modelView * vec4(vertexPosition.xyz, 1.0);
zValue = clipPosition.z / clipPosition.w;

The normalized device coordinates are in range [-1, 1]. The z component can linearly be mapped to the depth of the fragment. By default the depth range is [0, 1] (except it is changed by glDepthRange.
So in the fragment shader gl_FragDepth can be set by:

gl_FragDepth = zValue * 0.5 + 0.5;

If you use alpha Blending, then the Depth Test has to be disabled.

The objects behind other objects may not be drawn at all, because they are discarded by the depth test. Of course this depends on the drawing order. If the text which is covered is drawn first, then it will work. But if it is drawn last, then it is discarded. This explains the different behavior dependent on the direction of view.
Note, when blending is active, then the color doesn't affect the color buffer (if alpha is 0), but if the depth test is enable, then of course the depth is written to the depth buffer.

The only alternative is to draw the objects in sorted order form the back to the front. Of course the order depends on the direction of view, so the objects would have to be sorted per frame.




回答2:


I did some tests and to get the unwanted behaviour, I made some changes to your code by messing up the order of the entities used. As you know the order of the entities is important. In my example the text2DEntity "textFront" is put before the text2DEntity "textBack" in the hierarchy. So without changing your render environment we get something like this: I added a red sphere to test a little deeper.

I found a solution using a Forward renderer and without working with a depth buffer. Here is the result (of course, I didn't change the order of the entities):

We have to use a SortPolicy, so that the forward renderer knows which object is in front of another. This will change the order of entities compared to the distance of the camera and not on the hierarchy order of the qml file.

components: [
    RenderSettings {
        activeFrameGraph: SortPolicy {
            sortTypes: [
                SortPolicy.BackToFront
            ]

            ForwardRenderer {
                camera: camera
                clearColor: "black"
            }
        }
    },
    // Event Source will be set by the Qt3DQuickWindow
    InputSettings { }
]

Here is the full file content for easy testing:

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
        }
    }
}


来源:https://stackoverflow.com/questions/57627805/text2dentity-renders-opaque-and-hides-other-entities-behind-it

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!