Text2DEntity 呈现不透明并隐藏其后的其他实体

Posted

技术标签:

【中文标题】Text2DEntity 呈现不透明并隐藏其后的其他实体【英文标题】:Text2DEntity renders opaque and hides other entities behind it 【发布时间】:2019-08-23 13:54:21 【问题描述】:

我在 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
        
    

ma​​in.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 
        
    

ma​​in.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.vertdistancefieldtext.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 文本本身不透明并且文本之间的空间透明且独立于查看方向的任何想法?提前致谢。

编辑

我一定是无意中更改了示例中的某些内容,因为更改相机的位置不再显示预期的行为。我将在星期一访问我的工作环境时更正它。

更新

由于我的实体需要双面照明,我在RenderStateSet 中添加了一个额外的CullFace 组件,其中NoCulling 添加到了RenderStateSet,这解释了这种行为。我的 FrameGraph 看起来像这样:

components: [
    RenderSettings 
        activeFrameGraph: RenderStateSet 
            renderStates: [
                CullFace  mode: CullFace.NoCulling 
            ]
            ForwardRenderer 
                camera: camera
                clearColor: "transparent"
            
        
    ,

    InputSettings  
]

从背面查看时,由于实体是从后向前定义的,因此渲染是正确的。这在SortPolicy的文档中有明确说明:

"如果 FrameGraph 中不存在 QSortPolicy,则绘制实体 按照它们在实体层次结构中出现的顺序。”

将带有BackToFront 的附加SortPolicy 组件添加到FrameGraph 时,无论查看方向如何,渲染都是正确的。然后 FrameGraph 看起来像这样:

components: [
    RenderSettings 
        activeFrameGraph: SortPolicy 
            sortTypes: [ SortPolicy.BackToFront ]
            RenderStateSet 
                renderStates: [
                    CullFace  mode: CullFace.NoCulling 
                ]
                ForwardRenderer 
                    camera: camera
                    clearColor: "transparent"
                
            
        
    ,

    InputSettings  
]

【问题讨论】:

我怀疑 Qt3D 着色器是造成这种情况的原因。从手动测试中运行距离场示例时,文本呈现正常,这意味着我可以通过它查看我添加的球体。您是否对代码进行了其他更改?我试过了,但没有像你描述的那样得到同样的行为。好的,在您的示例中,我无法通过它们查看,但是更改相机位置时不会显示文本,因为我猜默认情况下 cullface 是关闭的。 @EddyAlleman 感谢您的评论,我添加了我得到的截图并解释了我期望的渲染行为。 不客气。您提到“从后面看场景时(将相机的位置更改为 Qt.vector3d(0,0,-40) )所有文本都可以渲染。”这是我不明白的部分。你真的从后面看到文字吗?当我更改线条时,我只能看到黄色球体: position: Qt.vector3d(0,0,-40) 而不是 // position: Qt.vector3d( 0.0, 0.0, 40 ) 你在任何地方改回剔除吗? @EddyAlleman ,是的,我有一个版本,我可以从背面看到文本,获得正确的绘图顺序,但不能从正面看到。但我无法访问我的工作环境,所以现在无法重现它。 好的,最好知道可能触发此行为的确切情况。 【参考方案1】:

问题似乎是这条线

zValue = vertexPosition.z;

在顶点着色器中。 vertexPosition 是模型空间中的坐标。如果要计算到相机的 z 距离,则必须通过 modelView 矩阵将坐标转换为视图空间:

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

注意,由于视图空间的 z 轴指向视口之外,因此必须反转坐标才能获得到相机的距离。


另一种可能性,对我来说似乎更正确,是计算剪辑空间坐标并进一步标准化设备坐标。裁剪空间坐标由模型视图矩阵(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,则必须禁用 Depth Test。

其他物体后面的物体可能根本不会被绘制,因为它们被深度测试丢弃了。 当然,这取决于绘图顺序。如果首先绘制所覆盖的文本,那么它将起作用。但如果它是最后绘制的,那么它就会被丢弃。这解释了取决于观察方向的不同行为。 请注意,当混合处于活动状态时,颜色不会影响颜色缓冲区(如果 alpha 为 0),但如果启用了深度测试,那么深度当然会写入深度缓冲区。

唯一的选择是按从后到前的排序顺序绘制对象。当然,顺序取决于观察方向,因此必须按帧对对象进行排序。

【讨论】:

感谢您的详细回答,不胜感激。我需要通过访问我的工作环境进行更彻底的测试,因为第一次快速测试没有发现任何变化。 @vre 我刚看到图片。我认为你有一个基本的误解。如果您使用 alpha Blending,则必须禁用深度测试!后面的对象根本不绘制,因为如果前面的对象之前绘制过,它们会被深度测试丢弃 谢谢你,我会尝试你的建议并设置一个不同的 Qt3D 框架图,禁用深度测试。【参考方案2】:

我做了一些测试,为了得到不需要的行为,我通过弄乱使用的实体的顺序对您的代码进行了一些更改。如您所知,实体的顺序很重要。 在我的示例中,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
        
    

【讨论】:

从here QSortPolicy 引用我的回答是不够的。您还需要两个框架图分支,一个用于不透明对象,一个用于透明对象,并首先绘制不透明的分支。 嗨,弗洛里安,感谢您在链接中提供的有用评论/彻底回答。您的回答与此处提出的问题有所不同。在这里,我们使用 text2DEntities,与甜甜圈相比,它与相机的距离很可能不同。我用动画做了一些测试,一旦 sortpolicy 发生变化(status1:text2 前面的 text1 -> status2:text1 前面的 text2),text2Dentities 就会保持它们围绕令牌的透明度。我的目标是提供一个尽可能简单的解决方案。你试过这个例子吗?它很容易玩和改变层次结构。 对不起,我的错!我没有意识到文本实体是二维的。如果更简单的解决方案有效,为什么要使用更复杂的解决方案,你是对的 :) @FlorianBlume 由于我的场景比显示的更复杂,我想我需要两个框架图分支。感谢您提及。

以上是关于Text2DEntity 呈现不透明并隐藏其后的其他实体的主要内容,如果未能解决你的问题,请参考以下文章

呈现一个模态视图控制器,但不要隐藏导航栏

如何在 iOS 中呈现一个半透明(半切)的视图控制器?

如何在不隐藏 tabBar 的情况下呈现视图控制器

使 SKScene 的背景透明不起作用......这是一个错误吗?

向上滚动时如何将一个div隐藏在另一个半透明的div后面

当组件在本机反应中重新呈现时,动态不透明度不会改变