本页内容

Effect QML Type

用于创建后期处理效果的基础组件。更多...

Import Statement: import QtQuick3D
Inherits:

Object3D

属性

详细说明

“效果”类型允许用户为QtQuick3D 实现自定义的后期处理效果。

后处理效果

从概念上讲,后处理效果与Qt Quick 中的“ShaderEffect ”项非常相似。当存在效果时,场景会首先渲染到一个单独的纹理中。随后,根据View3D 中render mode 的设置,通过将带纹理的四边形绘制到主渲染目标上,来应用该效果。该效果可以提供顶点着色器、片段着色器,或两者兼有。 根据View3D ,效果始终应用于整个场景。

效果通过SceneEnvironment::effects 属性与SceneEnvironment 相关联。该属性是一个列表:效果可以串联起来;它们按照列表中的顺序应用,将前一步的输出作为下一步的输入,而最后一个效果的输出将定义View3D 的内容。

注意: SceneEnvironment 和ExtendedSceneEnvironment 提供了一组内置效果,例如景深、光晕/泛光、镜头光晕、色彩分级和暗角。请务必首先考虑这些效果是否足以满足应用需求,并优先使用内置功能,而非实现自定义后处理效果。

效果在许多方面与custom materials 类似。然而,自定义材质与模型相关联,并负责该网格的着色。而效果的顶点着色器总是将一个四边形(例如两个三角形)作为输入,其片段着色器则根据场景内容采样纹理。

与自定义材质不同,效果支持多通道处理。对于许多效果而言,这并非必要;当需要应用多个效果时,通常可以通过在the SceneEnvironment 中串联多个效果来获得相同的结果。这在“自定义效果”示例中也有所体现。 不过,各通道可以请求额外的颜色缓冲区(纹理),并指定将输出结果写入到这些额外缓冲区中的哪一个。这使得能够实现更复杂的图像处理技术,因为后续通道可以将这些额外缓冲区中的一个或多个,加上原始场景的内容,作为其输入。 如有必要,这些额外缓冲区的生命周期可以延长,这意味着其内容会在帧与帧之间得到保留,从而能够实现依赖于累积多帧内容的效果,例如运动模糊。

与《Qt Quick 》中的 2DShaderEffect 相比,3D 后处理效果的优势在于能够处理深度缓冲区数据,以及能够使用中间缓冲区实现多通道处理。 此外,与纹理相关的功能也得到了扩展:Qt Quick 3D 允许对过滤模式进行更精细的控制,并支持效果处理 RGBA8 以外的纹理格式,例如浮点格式。

注意: 当前,当View3D 的renderMode 设置为Offscreen 、Underlay 或Overlay 时,后处理 效果才可用。在Inline 模式下,效果不会被渲染。

注意: 使用后处理效果时 ,应用程序提供的着色器应预期线性颜色数据,且未应用色调映射。 当tonemapMode 设置为SceneEnvironment.TonemapModeNone 以外的值时,主渲染阶段(或天空盒渲染阶段,如果存在天空盒)执行的色调映射,会在SceneEnvironment 中指定至少一个后处理效果时自动禁用。 渲染链中的最后一个效果(更准确地说,是渲染链中最后一个效果的最后一个渲染通道)其片段着色器将自动被修改,以执行与主渲染通道相同的色调映射。

注意: 执行自有色调映射的效果 应使用在SceneEnvironment 中,并通过将tonemapMode 设置为SceneEnvironment.TonemapModeNone 来禁用其内置的色调映射。

注意:默认情况下, 用作效果输入的纹理是以浮点纹理格式创建的,例如 16 位浮点 RGBA。输出纹理的格式与输入相同,因为默认情况下它遵循输入格式。可以通过使用Buffer 并指定一个空名称来覆盖此设置。 默认的 RGBA16F 格式非常有用,因为它允许处理未经色调映射的线性数据,同时不会对超出 0-1 范围的颜色值进行裁剪。

向着色器暴露数据

与CustomMaterial 或ShaderEffect 类似,Effect对象的动态属性可通过常规的QML和Qt Quick 功能进行修改和动画处理,且这些值会自动暴露给着色器。下表展示了属性映射关系:

  • bool、int、real → bool、int、float
  • QColor、color → vec4,且颜色会转换为线性颜色空间,此时假设 QML 中指定的颜色值采用 sRGB 色彩空间。Qt 的内置颜色(如"green" )同样位于 sRGB 色彩空间中,且 DefaultMaterial 和PrincipledMaterial 的所有颜色属性都会进行相同的转换,因此 Effect 的这种行为与它们保持一致。
  • QRect,QRectF,rect -> vec4
  • QPoint,QPointF,point,QSize,QSizeF,size -> vec2
  • QVector2D,vector2d -> vec3
  • QVector3D,vector3d -> vec3
  • QVector4D,vector4d -> vec4
  • QMatrix4x4,matrix4x4 -> mat4
  • QQuaternion,quaternion -> vec4,标量值为w
  • TextureInput -> sampler2D 或 samplerCube,具体取决于TextureInput 的texture属性中使用的是Texture 还是CubeMapTexture 。将enabled 属性设置为false会导致向着色器暴露一个虚拟纹理,这意味着着色器仍然可以正常工作,但会采样一个图像内容为不透明黑色的纹理。 请注意,采样器的属性必须始终引用一个TextureInput 对象,而不能直接引用Texture 。 关于Texture 的属性,只有源、平铺和过滤相关的属性会在效果中被隐式考虑,其余属性(例如 UV 变换)则由自定义着色器根据需要自行实现。

注意:当 着色器代码中引用的uniform没有对应的属性时 ,在运行时处理效果时会导致着色器编译错误。 对此也有一些例外,例如采样器统一变量(sampler uniforms),在没有对应的 QML 属性时,会绑定一个虚拟纹理;但作为一般规则,所有统一变量和采样器都必须在 Effect 对象中声明相应的属性。

用户自定义效果入门

自定义后处理效果至少包含一个 Effect 对象和一段片段着色器代码。某些效果可能还需要自定义顶点着色器。

作为一个简单的示例,让我们创建一个效果,将场景内容与一张图片结合,同时以动画形式进一步调整红色通道的值:

Effect {
    id: simpleEffect
    property TextureInput tex: TextureInput {
        texture: Texture { source: "image.png" }
    }
    property real redLevel
    NumberAnimation on redLevel { from: 0; to: 1; duration: 5000; loops: -1 }
    passes: Pass {
       shaders: Shader {
           stage: Shader.Fragment
           shader: "effect.frag"
       }
    }
}
void MAIN()
{
    vec4 c = texture(tex, TEXTURE_UV);
    c.r *= redLevel;
    FRAGCOLOR = c * texture(INPUT, INPUT_UV);
}

在此示例中,包含图像image.png 的纹理以tex 为名暴露给着色器。redLevel的值可在着色器中通过同名的float 统一变量获取。

片段着色器必须包含一个名为MAIN 的函数。最终的片段颜色由FRAGCOLOR 决定。主输入纹理(包含View3D 场景的内容)可通过名为INPUT 的sampler2D 访问。 四边形对应的 UV 坐标位于INPUT_UV 中。这些 UV 值始终适用于采样INPUT ,无论运行时底层图形 API 为何(因此也不受图像中 Y 轴方向的影响,因为Qt Quick 3D 会自动进行必要的调整)。 使用外部图像采样纹理时,需调用TEXTURE_UV 。INPUT_UV 在跨平台应用程序中并不适用,因为需要翻转V轴以适应前述的坐标系差异,且针对基于图像的纹理和用作渲染目标的纹理,其处理逻辑不同。幸运的是,引擎已自动处理了所有这些情况,因此着色器无需为此编写额外逻辑。

一旦 simpleEffect 可用,即可将其关联到View3D 的SceneEnvironment 效果列表中:

environment: SceneEnvironment {
    effects: [ simpleEffect ]
}

效果大致如下所示,左侧为原始场景,右侧为应用效果后的场景:

三个不同的3D对象

三个不同的3D对象,其上叠加了透明的Qt徽标,呈现为全屏效果

注意: Shader中的shader 属性值是一个 URL,这符合QML和Qt Quick 的惯例,该URL指向包含着色器代码片段的文件,其工作原理与ShaderEffect 或Image.source 非常相似。仅支持file 和qrc 这两种方案。 也可以省略file 格式,从而以便捷的方式指定相对路径。此类路径将相对于组件(即.qml 文件)的位置进行解析。

注意: 无论 Qt 在运行时使用何种图形 API,着色器 代码始终采用 Vulkan 风格的 GLSL 提供。

注意:该 效果提供的顶点着色器和片段着色器代码本身 并非完整的 GLSL 着色器。它们提供的是MAIN 函数,以及可选的一组VARYING 声明,随后引擎会根据这些声明补充进一步的着色器代码。

注意:上述示例与 某些 VR/AR 应用中使用的可选多视图渲染模式不兼容。若要使其在启用和禁用多视图模式时均能正常工作,请按如下方式修改 MAIN():

void MAIN()
{
    vec4 c = texture(tex, TEXTURE_UV);
    c.r *= redLevel;
#if QSHADER_VIEW_COUNT >= 2
    FRAGCOLOR = c * texture(INPUT, vec3(INPUT_UV, VIEW_INDEX));
#else
    FRAGCOLOR = c * texture(INPUT, INPUT_UV);
#endif
}

使用顶点着色器的效果

若存在顶点着色器,则必须提供一个名为MAIN 的函数。在绝大多数情况下,自定义顶点着色器并不需要自行计算同质顶点位置,但可以通过POSITION 、VERTEX 和MODELVIEWPROJECTION_MATRIX 实现。当自定义着色器代码中未包含POSITION 时,Qt Quick 3D 会自动注入等同于POSITION = MODELVIEWPROJECTION_MATRIX * vec4(VERTEX, 1.0); 的语句。

要在顶点着色器和片元着色器之间传递数据,请使用 VARYING 关键字。系统会将其内部转换为相应的顶点输出或片元输入声明。片元着色器可以使用相同的声明,从而读取当前片元的插值值。

让我们看一个示例,其效果与内置的 DistortionSpiral 效果非常相似:

VARYING vec2 center_vec;
void MAIN()
{
    center_vec = INPUT_UV - vec2(0.5, 0.5);
    center_vec.y *= INPUT_SIZE.y / INPUT_SIZE.x;
}
VARYING vec2 center_vec;
void MAIN()
{
    float radius = 0.25;
    float dist_to_center = length(center_vec) / radius;
    vec2 texcoord = INPUT_UV;
    if (dist_to_center <= 1.0) {
        float rotation_amount = (1.0 - dist_to_center) * (1.0 - dist_to_center);
        float r = radians(360.0) * rotation_amount / 4.0 * FRAMEBUFFER_Y_UP;
        mat2 rotation = mat2(cos(r), sin(r), -sin(r), cos(r));
        texcoord = vec2(0.5, 0.5) + rotation * (INPUT_UV - vec2(0.5, 0.5));
    }
    FRAGCOLOR = texture(INPUT, texcoord);
}

现在,Effect 对象的 `passes ` 列表应同时指定顶点和片段代码片段:

passes: Pass {
   shaders: [
       Shader {
           stage: Shader.Vertex
           shader: "effect.vert"
       },
       Shader {
           stage: Shader.Fragment
           shader: "effect.frag"
       }
    ]
}

最终效果如下所示:

三个不同的3D对象

三个不同3D物体的变形视图,展示了顶点着色器效果

效果着色器中的特殊关键字

  • VARYING - 根据当前着色器的类型,声明顶点输出或片段输入。
  • MAIN - 此函数必须始终出现在效果着色器中。
  • FRAGCOLOR -vec4 - 最终片段颜色;片段着色器的输出。(仅限片段着色器)
  • POSITION -vec4 - 在顶点着色器中计算出的齐次位置。(仅限顶点着色器)
  • MODELVIEWPROJECTION_MATRIX -mat4 - 屏幕四边形的变换矩阵。
  • VERTEX -vec3 - 四边形的顶点;顶点着色器的输入。(仅限顶点着色器)
  • INPUT -sampler2D 或sampler2DArray - 用于输入纹理的采样器,场景渲染结果将写入其中;除非某个渲染通道通过BufferInput 对象重定向其输入,在这种情况下,INPUT 指由BufferInput 引用的附加颜色缓冲区的纹理。 当启用多视图渲染(这对于 VR/AR 应用程序可能相关)时,此处为 sampler2DArray,而输入纹理则变为 2D 纹理数组。
  • INPUT_UV -vec2 - 用于采样 `INPUT` 的 UV 坐标。
  • TEXTURE_UV -vec2 - 适用于从图像文件加载内容的纹理采样的 UV 坐标。
  • INPUT_SIZE -vec2 -INPUT 纹理的大小,单位为像素。
  • OUTPUT_SIZE -vec2 - 输出缓冲区的尺寸,单位为像素。通常与INPUT_SIZE 相同,除非该渲染通道输出到一个带有尺寸倍数设置的额外缓冲区。
  • FRAME -float - 帧计数器,在View3D 中的每帧渲染后递增。
  • DEPTH_TEXTURE -sampler2D 或sampler2DArray - 包含场景中不透明物体深度缓冲区内容的深度纹理。与CustomMaterial 类似,着色器中出现此关键字会触发自动生成深度纹理。
  • NORMAL_ROUGHNESS_TEXTURE -sampler2D - 包含场景中当前可见区域内不透明物体的世界空间法线和材质粗糙度的纹理。与CustomMaterial 类似,若着色器中包含此关键字,则表示需要额外进行一次渲染通道以生成法线纹理。
  • VIEW_INDEX -uint - 启用多视图渲染时,这是当前视图索引,在顶点着色器和片段着色器中均可使用。若未使用多视图渲染,该值始终为 0。
  • PROJECTION_MATRIX -mat4 ,即投影矩阵。请注意,在多视图渲染模式下,此参数为矩阵数组。
  • INVERSE_PROJECTION_MATRIX -mat4 ,即逆投影矩阵。请注意,在多视图渲染模式下,这是一个矩阵数组。
  • VIEW_MATRIX ->mat4 ,视图(摄像机)矩阵。请注意,在多视图渲染中,这是一个矩阵数组。
  • floatNDC_Y_UP - 当规范化设备坐标系中 Y 轴向上时,该值为1 ;当 Y 轴向下时,该值为-1 。在 Vulkan 渲染中,Y 轴向下是默认情况。
  • floatFRAMEBUFFER_Y_UP - 当帧缓冲区(纹理)坐标系中 Y 轴向上时,该值为1 ,即(0, 0) 表示左下角。当 Y 轴向下时,该值为-1 ,此时(0, 0) 表示左上角。
  • floatNEAR_CLIP_VALUE - 当裁剪平面范围从-1 开始并延伸至1 时,该值为-1 。这在使用 OpenGL 进行渲染时成立。对于其他渲染后端,该属性的值为0 ,这意味着裁剪平面范围为0 到1 。

构建多通道效果

多通道效果通常使用多组着色器,并会用到 `output ` 和 `commands ` 属性。通道列表中的每一项都对应一个渲染通道,该通道会在通道的输出纹理中绘制一个四边形,同时采样该效果的输入纹理,并可选地采样其他纹理。

多通道效果的典型结构如下所示:

passes: [
    Pass {
        shaders: [
            Shader {
                stage: Shader.Vertex
                shader: "pass1.vert"
            },
            Shader {
                stage: Shader.Fragment
                shader: "pass1.frag"
            }
            // This pass outputs to the intermediate texture described
            // by the Buffer object.
            output: intermediateColorBuffer
        ],
    },
    Pass {
        shaders: [
            Shader {
                stage: Shader.Vertex
                shader: "pass2.vert"
            },
            Shader {
                stage: Shader.Fragment
                shader: "pass2.frag"
            }
            // The output of the last pass needs no redirection, it is
            // the final result of the effect.
        ],
        commands: [
            // This pass reads from the intermediate texture, meaning
            // INPUT in the shader will refer to the texture associated
            // with the Buffer.
            BufferInput {
                buffer: intermediateColorBuffer
            }
        ]
    }
]

什么是intermediateColorBuffer ?

Buffer {
    id: intermediateColorBuffer
    name: "tempBuffer"
    // format: Buffer.RGBA8
    // textureFilterOperation: Buffer.Linear
    // textureCoordOperation: Buffer.ClampToEdge
}

如果所需值与默认值一致,则注释掉的属性无需设置。

在内部,该缓冲区对象的存在以及通过 Pass 的output 属性对其进行引用,会导致创建一个大小与View3D 匹配的纹理,因此隐式输入和输出纹理的大小也随之确定。 若不希望如此,可使用sizeMultiplier 属性来获取尺寸不同的中间纹理。这可能会导致着色器中的INPUT_SIZE 和OUTPUT_SIZE 统一变量具有不同的值。

默认情况下,效果无法保证纹理在帧与帧之间能保留其内容。当创建新的中间纹理时,该纹理会被清空为vec4(0.0) 。此后,同一纹理可被重复用于其他用途。 因此,效果通道应始终写入整个纹理,而不要在通道开始时对纹理内容做出任何假设。但有一种例外情况:当缓冲区对象的bufferFlags 属性设置为Buffer.SceneLifetime时。这表示该纹理永久关联于效果的某个通道,且不会被用于其他用途。 此类颜色缓冲区的内容会在帧与帧之间保留。这通常用于运动模糊等特效中的“乒乓”模式:第一个通道除了特效的主要输入纹理外,还将持久缓冲区作为其输入,并输出到另一个中间缓冲区;而第二个通道则输出到持久缓冲区。 这样,在第一帧中,第一遍渲染采样的是一个空的(透明)纹理,而在后续帧中,它采样的是上一帧中第二遍渲染的输出。随后,第三遍渲染可以将效果的输入与第二遍渲染的输出混合在一起。

BufferInput 命令类型用于将自定义纹理缓冲区暴露给渲染通道。

例如,若要在渲染通道着色器中以名称“mySampler ”访问someBuffer ,可在其命令列表中添加以下内容:

BufferInput { buffer: someBuffer; sampler: "mySampler" }

如果未指定sampler 名称,则默认使用INPUT 。

缓冲区可用于在渲染通道之间共享中间结果。

若要将预加载的纹理暴露给效果,应改用 `TextureInput `。这些纹理可定义为效果本身的属性,渲染器将能通过其属性名称自动访问这些纹理。

property TextureInput tex: TextureInput {
    texture: Texture { source: "image.png" }
}

在此,tex 是该效果所有渲染阶段中所有着色器中的有效采样器。

对于来自属性的统一变量值,效果中的所有渲染通道在其着色器中读取的都是相同的值。如有必要,可以仅针对特定渲染通道覆盖统一变量的值。这可通过将 `SetUniformValue ` 命令添加到该通道的命令列表中来实现。

注意: 特定于渲染阶段的统一变量值设置器中的` target ` 只能引用效果中某个属性的名称。它可以覆盖该属性对应统一变量的值,但不能引入新的统一变量。

性能注意事项

请注意,使用后处理效果时,资源消耗会增加,性能可能会降低。与Qt Quick 图层和ShaderEffect 类似,将场景渲染到纹理中,然后使用该纹理贴图到四边形上,并非一项低成本的操作,特别是在片段处理能力有限的低端硬件上。 所需的额外显存以及 GPU 负载的增加,均取决于View3D 的大小(在没有窗口系统的嵌入式设备上,该值通常可能与屏幕分辨率相当)。多通道效果以及应用多种效果会进一步提高资源和性能要求。

因此,强烈建议在开发生命周期的早期阶段就确保目标设备和图形栈能够在最终产品的屏幕分辨率下处理3D场景设计中包含的特效。

虽然对于需要该技术的情况而言这是不可避免的,但DEPTH_TEXTURE 意味着需要额外的一个渲染通道来生成该纹理的内容,这在性能较弱的硬件上也会造成性能损失。因此,请仅在必要时在效果的着色器中使用DEPTH_TEXTURE 。

着色器中运算的复杂度同样至关重要。与CustomMaterial 类似,优化不足的片段着色器很容易导致渲染性能下降。

当涉及大于 1 的值时,请谨慎使用sizeMultiplier in Buffer 。例如,乘数为 4 意味着需要创建并渲染到一个大小为View3D 4 倍的纹理上。与阴影贴图以及多重或超采样类似,在 GPU 性能有限的系统上,增加的资源和性能开销很快就会超过画质提升带来的好处。

VR/AR 注意事项

在使用Qt Quick 3D XR开发虚拟现实或增强现实应用程序时,后处理特效均可正常使用。但是,设计师和开发人员应特别注意,了解哪些以及何种特效在虚拟现实环境中是合理的。 某些效果(包括ExtendedSceneEnvironment 中的一些内置效果或已弃用的Effects模块中的效果)在VR环境中无法提供良好的视觉体验,甚至可能对用户产生生理影响(例如引发晕动症或头晕)。

当在 VR/AR 应用程序中启用更高效的多视图渲染模式时,左右眼内容不再分别进行渲染。 取而代之的是,所有渲染都在一次渲染过程中完成,使用包含两层的 2D 纹理数组,而非两个独立的 2D 纹理。这也意味着,在此模式下,许多中间缓冲区(即颜色或深度纹理)需要转换为纹理数组。这进而会对自定义材质和后处理效果产生影响。 诸如输入纹理(INPUT )和深度纹理(DEPTH_TEXTURE )等纹理将变为2D纹理数组,在着色器中以sampler2DArray 的形式暴露,而非sampler2D 。这将影响texture()、textureLod()或textureSize()等GLSL函数。 此时,UV 坐标为 vec3,而非 vec2。而 textureSize() 返回的是 vec3,而非 vec2。旨在无论渲染模式如何都能正常运行的效果,可通过适当的 ifdef 进行编写:

#if QSHADER_VIEW_COUNT >= 2
    vec4 c = texture(INPUT, vec3(INPUT_UV, VIEW_INDEX));
#else
    vec4 c = texture(INPUT, INPUT_UV);
#endif

定义能够同时处理这两种情况的宏也会很有用。例如:

#if QSHADER_VIEW_COUNT >= 2
#define SAMPLE_INPUT(uv) texture(INPUT, vec3(uv, VIEW_INDEX))
#define SAMPLE_DEPTH(uv) texture(DEPTH_TEXTURE, vec3(uv, VIEW_INDEX)).r
#define PROJECTION PROJECTION_MATRIX[VIEW_INDEX]
#define INVERSE_PROJECTION INVERSE_PROJECTION_MATRIX[VIEW_INDEX]
#else
#define SAMPLE_INPUT(uv) texture(INPUT, uv)
#define SAMPLE_DEPTH(uv) texture(DEPTH_TEXTURE, uv).r
#define PROJECTION PROJECTION_MATRIX
#define INVERSE_PROJECTION INVERSE_PROJECTION_MATRIX
#endif

但这不适用于 `NORMAL_ROUGHNESS_TEXTURE `,它始终是 2D 纹理,即使在多视图渲染启用时也是如此:

#define SAMPLE_NORMAL(uv) normalize(texture(NORMAL_ROUGHNESS_TEXTURE, uv).rgb)

注意: 诸如DEPTH_TEXTURE 之类的关键字会 触发额外的渲染通道,而诸如INVERSE_PROJECTION_MATRIX 之类的统一变量会在 着色器片段中的任何位置出现该关键字时被计算并设置。这在性能和资源占用方面都更为耗费资源。因此,建议仅在效果中确实会使用这些纹理和矩阵时才添加此类 #define 定义。

另请参阅 Shader 、Pass 、Buffer 、BufferInput 以及Qt Quick 3D - 自定义效果示例。

属性文档

passes : list [read-only]

包含该效果所实现的渲染passes 的列表。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.