如何在Qt3D中绘制对象的轮廓?

如何在Qt3D中绘制对象的轮廓?,qt,opengl,graphics,3d,qt3d,Qt,Opengl,Graphics,3d,Qt3d,如何在Qt3D中的任何其他对象上绘制对象的轮廓?例如,要在3D编辑器中高亮显示选定对象?如果要始终绘制实体的轮廓,即使该实体位于其他实体之后,一种解决方案是分两步进行: 把一切都画成正常的样子 仅绘制选定对象的轮廓 绘制轮廓时,需要使用轮廓效果,该效果可以在两个渲染过程中实现: import QtQuick 2.2 as QQ2 import Qt3D.Core 2.0 import Qt3D.Render 2.0 import Qt3D.Input 2.0 import Qt3D.Extras

如何在Qt3D中的任何其他对象上绘制对象的轮廓?例如,要在3D编辑器中高亮显示选定对象?

如果要始终绘制实体的轮廓,即使该实体位于其他实体之后,一种解决方案是分两步进行:

  • 把一切都画成正常的样子
  • 仅绘制选定对象的轮廓
  • 绘制轮廓时,需要使用轮廓效果,该效果可以在两个渲染过程中实现:

    import QtQuick 2.2 as QQ2
    import Qt3D.Core 2.0
    import Qt3D.Render 2.0
    import Qt3D.Input 2.0
    import Qt3D.Extras 2.0
    
    Entity {
        Camera {
            id: camera
            projectionType: CameraLens.PerspectiveProjection
            fieldOfView: 45
            aspectRatio: 16/9
            nearPlane : 0.1
            farPlane : 1000.0
            position: Qt.vector3d( 0.0, 0.0, -40.0 )
            upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
            viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
        }
    
        OrbitCameraController {
            camera: camera
        }
    
        components: [
            RenderSettings {
                activeFrameGraph: RenderSurfaceSelector {
                    id: surfaceSelector
                    Viewport {
                        CameraSelector {
                            camera: camera
                            FrustumCulling {
                                TechniqueFilter {
                                    matchAll: [
                                        FilterKey { name: "renderingStyle"; value: "forward" }
                                    ]
                                    ClearBuffers {
                                        clearColor: Qt.rgba(0.1, 0.2, 0.3)
                                        buffers: ClearBuffers.ColorDepthStencilBuffer
                                    }
                                }
                                TechniqueFilter {
                                    matchAll: [
                                        FilterKey { name: "renderingStyle"; value: "outline" }
                                    ]
                                    RenderPassFilter {
                                        matchAny: [
                                            FilterKey {
                                                name: "pass"; value: "geometry"
                                            }
                                        ]
                                        ClearBuffers {
                                            buffers: ClearBuffers.ColorDepthStencilBuffer
                                            RenderTargetSelector {
                                                target: RenderTarget {
                                                    attachments : [
                                                        RenderTargetOutput {
                                                            objectName : "color"
                                                            attachmentPoint : RenderTargetOutput.Color0
                                                            texture : Texture2D {
                                                                id : colorAttachment
                                                                width : surfaceSelector.surface.width
                                                                height : surfaceSelector.surface.height
                                                                format : Texture.RGBA32F
                                                            }
                                                        }
                                                    ]
                                                }
                                            }
                                        }
                                    }
                                    RenderPassFilter {
                                        parameters: [
                                            Parameter { name: "color"; value: colorAttachment },
                                            Parameter { name: "winSize"; value : Qt.size(surfaceSelector.surface.width, surfaceSelector.surface.height) }
                                        ]
                                        matchAny: [
                                            FilterKey {
                                                name: "pass"; value: "outline"
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    }
                }
            },
            InputSettings { }
        ]
    
        PhongMaterial {
            id: material
        }
    
        Material {
            id: outlineMaterial
    
            effect: Effect {
                techniques: [
                    Technique {
                        graphicsApiFilter {
                            api: GraphicsApiFilter.OpenGL
                            majorVersion: 3
                            minorVersion: 1
                            profile: GraphicsApiFilter.CoreProfile
                        }
    
                        filterKeys: [
                            FilterKey { name: "renderingStyle"; value: "outline" }
                        ]
                        renderPasses: [
                            RenderPass {
                                filterKeys: [
                                    FilterKey { name: "pass"; value: "geometry" }
                                ]
                                shaderProgram: ShaderProgram {
                                    vertexShaderCode: "
    #version 150 core
    
    in vec3 vertexPosition;
    
    uniform mat4 modelViewProjection;
    
    void main()
    {
        gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 );
    }
    "
    
                                    fragmentShaderCode: "
    #version 150 core
    
    out vec4 fragColor;
    
    void main()
    {
        fragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
    }
    "
                                }
                            }
                        ]
                    }
                ]
            }
        }
    
        SphereMesh {
            id: sphereMesh
            radius: 3
        }
    
        Transform {
            id: sphereTransform
        }
    
        Transform {
            id: sphereTransform2
            // TODO workaround because the transform cannot be shared
            matrix: sphereTransform.matrix
        }
    
        Entity {
            id: sphereEntity
            components: [ sphereMesh, material, sphereTransform ]
        }
    
        Entity {
            id: sphereOutlineEntity
            components: [ sphereMesh, outlineMaterial, sphereTransform2 ]
        }
    
        Entity {
            id: outlineQuad
            components: [
                PlaneMesh {
                    width: 2.0
                    height: 2.0
                    meshResolution: Qt.size(2, 2)
                },
                Transform {
                    rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 90)
                },
                Material {
                    effect: Effect {
                        techniques: [
                            Technique {
                                filterKeys: [
                                    FilterKey { name: "renderingStyle"; value: "outline" }
                                ]
                                graphicsApiFilter {
                                    api: GraphicsApiFilter.OpenGL
                                    profile: GraphicsApiFilter.CoreProfile
                                    majorVersion: 3
                                    minorVersion: 1
                                }
                                renderPasses : RenderPass {
                                    filterKeys : FilterKey { name : "pass"; value : "outline" }
                                    shaderProgram : ShaderProgram {
                                        vertexShaderCode: "
    #version 150
    
    in vec4 vertexPosition;
    uniform mat4 modelMatrix;
    
    void main()
    {
        gl_Position = modelMatrix * vertexPosition;
    }
    "
    
                                        fragmentShaderCode: "
    #version 150
    
    uniform sampler2D color;
    uniform vec2 winSize;
    
    out vec4 fragColor;
    
    void main()
    {
    
        int lineWidth = 5;
    
        vec2 texCoord = gl_FragCoord.xy / winSize;
        vec2 texCoordUp = (gl_FragCoord.xy + vec2(0, lineWidth)) / winSize;
        vec2 texCoordDown = (gl_FragCoord.xy + vec2(0, -lineWidth)) / winSize;
        vec2 texCoordRight = (gl_FragCoord.xy + vec2(lineWidth, 0)) / winSize;
        vec2 texCoordLeft = (gl_FragCoord.xy + vec2(-lineWidth, 0)) / winSize;
    
        vec4 col = texture(color, texCoord);
        vec4 colUp = texture(color, texCoordUp);
        vec4 colDown = texture(color, texCoordDown);
        vec4 colRight = texture(color, texCoordRight);
        vec4 colLeft = texture(color, texCoordLeft);
    
        if ((colUp == colDown && colRight == colLeft) || col.a == 0.0)
            discard;
    
        fragColor = col;
    }
    "
                                    }
                                }
                            }]
                    }
                }
    
            ]
        }
    }
    
  • 使用简单的颜色着色器将几何体渲染为纹理
  • 使用着色器渲染到屏幕,该着色器获取纹理中的每个像素并比较周围的像素。如果它们相等,我们就在对象内部,可以丢弃片段。如果它们不同,我们就在物体的边缘,我们应该画颜色
  • 以下是上述着色器的简单实现:

    #version 150
    
    uniform sampler2D color;
    uniform vec2 winSize;
    
    out vec4 fragColor;
    
    void main()
    {
    
        int lineWidth = 5;
    
        vec2 texCoord = gl_FragCoord.xy / winSize;
        vec2 texCoordUp = (gl_FragCoord.xy + vec2(0, lineWidth)) / winSize;
        vec2 texCoordDown = (gl_FragCoord.xy + vec2(0, -lineWidth)) / winSize;
        vec2 texCoordRight = (gl_FragCoord.xy + vec2(lineWidth, 0)) / winSize;
        vec2 texCoordLeft = (gl_FragCoord.xy + vec2(-lineWidth, 0)) / winSize;
    
        vec4 col = texture(color, texCoord);
        vec4 colUp = texture(color, texCoordUp);
        vec4 colDown = texture(color, texCoordDown);
        vec4 colRight = texture(color, texCoordRight);
        vec4 colLeft = texture(color, texCoordLeft);
    
        if ((colUp == colDown && colRight == colLeft) || col.a == 0.0)
            discard;
    
        fragColor = col;
    }
    
    注意:最好是取两个值之间的差值,而不是使用等式

    使用这种方法,您不必担心深度测试和绘制对象的顺序:第二次绘制时,您将始终在所有其他内容之上进行绘制

    可以通过使用两种具有不同过滤器关键帧的技术添加单一效果来实现这一点。或者,如果要使用Qt3D.Extras中的材质,可以添加另一个具有相同变换和网格的实体以及使用轮廓技术的材质

    下面是一个使用两个渲染过程在所有其他对象上绘制轮廓的示例:

    import QtQuick 2.2 as QQ2
    import Qt3D.Core 2.0
    import Qt3D.Render 2.0
    import Qt3D.Input 2.0
    import Qt3D.Extras 2.0
    
    Entity {
        Camera {
            id: camera
            projectionType: CameraLens.PerspectiveProjection
            fieldOfView: 45
            aspectRatio: 16/9
            nearPlane : 0.1
            farPlane : 1000.0
            position: Qt.vector3d( 0.0, 0.0, -40.0 )
            upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
            viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
        }
    
        OrbitCameraController {
            camera: camera
        }
    
        components: [
            RenderSettings {
                activeFrameGraph: RenderSurfaceSelector {
                    id: surfaceSelector
                    Viewport {
                        CameraSelector {
                            camera: camera
                            FrustumCulling {
                                TechniqueFilter {
                                    matchAll: [
                                        FilterKey { name: "renderingStyle"; value: "forward" }
                                    ]
                                    ClearBuffers {
                                        clearColor: Qt.rgba(0.1, 0.2, 0.3)
                                        buffers: ClearBuffers.ColorDepthStencilBuffer
                                    }
                                }
                                TechniqueFilter {
                                    matchAll: [
                                        FilterKey { name: "renderingStyle"; value: "outline" }
                                    ]
                                    RenderPassFilter {
                                        matchAny: [
                                            FilterKey {
                                                name: "pass"; value: "geometry"
                                            }
                                        ]
                                        ClearBuffers {
                                            buffers: ClearBuffers.ColorDepthStencilBuffer
                                            RenderTargetSelector {
                                                target: RenderTarget {
                                                    attachments : [
                                                        RenderTargetOutput {
                                                            objectName : "color"
                                                            attachmentPoint : RenderTargetOutput.Color0
                                                            texture : Texture2D {
                                                                id : colorAttachment
                                                                width : surfaceSelector.surface.width
                                                                height : surfaceSelector.surface.height
                                                                format : Texture.RGBA32F
                                                            }
                                                        }
                                                    ]
                                                }
                                            }
                                        }
                                    }
                                    RenderPassFilter {
                                        parameters: [
                                            Parameter { name: "color"; value: colorAttachment },
                                            Parameter { name: "winSize"; value : Qt.size(surfaceSelector.surface.width, surfaceSelector.surface.height) }
                                        ]
                                        matchAny: [
                                            FilterKey {
                                                name: "pass"; value: "outline"
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    }
                }
            },
            InputSettings { }
        ]
    
        PhongMaterial {
            id: material
        }
    
        Material {
            id: outlineMaterial
    
            effect: Effect {
                techniques: [
                    Technique {
                        graphicsApiFilter {
                            api: GraphicsApiFilter.OpenGL
                            majorVersion: 3
                            minorVersion: 1
                            profile: GraphicsApiFilter.CoreProfile
                        }
    
                        filterKeys: [
                            FilterKey { name: "renderingStyle"; value: "outline" }
                        ]
                        renderPasses: [
                            RenderPass {
                                filterKeys: [
                                    FilterKey { name: "pass"; value: "geometry" }
                                ]
                                shaderProgram: ShaderProgram {
                                    vertexShaderCode: "
    #version 150 core
    
    in vec3 vertexPosition;
    
    uniform mat4 modelViewProjection;
    
    void main()
    {
        gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 );
    }
    "
    
                                    fragmentShaderCode: "
    #version 150 core
    
    out vec4 fragColor;
    
    void main()
    {
        fragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
    }
    "
                                }
                            }
                        ]
                    }
                ]
            }
        }
    
        SphereMesh {
            id: sphereMesh
            radius: 3
        }
    
        Transform {
            id: sphereTransform
        }
    
        Transform {
            id: sphereTransform2
            // TODO workaround because the transform cannot be shared
            matrix: sphereTransform.matrix
        }
    
        Entity {
            id: sphereEntity
            components: [ sphereMesh, material, sphereTransform ]
        }
    
        Entity {
            id: sphereOutlineEntity
            components: [ sphereMesh, outlineMaterial, sphereTransform2 ]
        }
    
        Entity {
            id: outlineQuad
            components: [
                PlaneMesh {
                    width: 2.0
                    height: 2.0
                    meshResolution: Qt.size(2, 2)
                },
                Transform {
                    rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 90)
                },
                Material {
                    effect: Effect {
                        techniques: [
                            Technique {
                                filterKeys: [
                                    FilterKey { name: "renderingStyle"; value: "outline" }
                                ]
                                graphicsApiFilter {
                                    api: GraphicsApiFilter.OpenGL
                                    profile: GraphicsApiFilter.CoreProfile
                                    majorVersion: 3
                                    minorVersion: 1
                                }
                                renderPasses : RenderPass {
                                    filterKeys : FilterKey { name : "pass"; value : "outline" }
                                    shaderProgram : ShaderProgram {
                                        vertexShaderCode: "
    #version 150
    
    in vec4 vertexPosition;
    uniform mat4 modelMatrix;
    
    void main()
    {
        gl_Position = modelMatrix * vertexPosition;
    }
    "
    
                                        fragmentShaderCode: "
    #version 150
    
    uniform sampler2D color;
    uniform vec2 winSize;
    
    out vec4 fragColor;
    
    void main()
    {
    
        int lineWidth = 5;
    
        vec2 texCoord = gl_FragCoord.xy / winSize;
        vec2 texCoordUp = (gl_FragCoord.xy + vec2(0, lineWidth)) / winSize;
        vec2 texCoordDown = (gl_FragCoord.xy + vec2(0, -lineWidth)) / winSize;
        vec2 texCoordRight = (gl_FragCoord.xy + vec2(lineWidth, 0)) / winSize;
        vec2 texCoordLeft = (gl_FragCoord.xy + vec2(-lineWidth, 0)) / winSize;
    
        vec4 col = texture(color, texCoord);
        vec4 colUp = texture(color, texCoordUp);
        vec4 colDown = texture(color, texCoordDown);
        vec4 colRight = texture(color, texCoordRight);
        vec4 colLeft = texture(color, texCoordLeft);
    
        if ((colUp == colDown && colRight == colLeft) || col.a == 0.0)
            discard;
    
        fragColor = col;
    }
    "
                                    }
                                }
                            }]
                    }
                }
    
            ]
        }
    }
    
    结果是:


    渲染原始几何体时,可以创建遮罩。此遮罩可用于创建轮廓。因此,可以跳过将几何体渲染为纹理的额外过程。是的,这是一个很好的选择。这真的取决于你想概述什么。您描述的内容将突出显示物理轮廓,包括同一对象内的轮廓,例如,这对于卡通效果非常有用。上述答案给出的效果更接近于在3D编辑器(如Blender)或游戏(如《帝国时代》)中选择对象时绘制的轮廓。如果两个轮廓对象重叠,上述解决方案将绘制轮廓,就像两个对象是相同的对象一样,这有时是您想要的。模具测试可以这样设置,即遮罩将包含与您答案步骤1)中的纹理相同的信息。这一点很好。这将使丢弃的测试更加严格。或者您是否考虑过使用模具而不是其他纹理的另一个好处?这是因为表达式是在初始化
    surfaceSelector.surface
    之前计算的。要绕过它,可以将其更改为
    surfaceSelector.surface?surfaceSelector.height:100
    (或您首选的其他默认数字)。关于偏移量,这是因为纹理大小没有考虑Mac上的视网膜显示。要解决此问题,您可以使用
    Screen.devicePixelRatio
    将宽度和高度相乘。