Effect QML Type
用于创建后期处理效果的基本组件。更多
Import Statement: | import QtQuick3D |
Inherits: |
属性
- passes : list
详细说明
效果类型允许用户为QtQuick3D 实现自己的后处理效果。
后处理效果
后处理效果在概念上与Qt Quick 的ShaderEffect 项非常相似。当效果出现时,场景会首先渲染为一个单独的纹理。然后,根据render mode 的View3D ,通过在主渲染目标上绘制纹理四边形来应用效果。效果可以提供顶点着色器、片段着色器或两者。效果总是应用于整个场景,每个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 一样,效果对象的动态属性可使用常用的 QML 和Qt Quick 工具进行更改和动画,其值会自动显示给着色器。以下列表显示了属性的映射方式:
- bool, int, real -> bool, int, float
- QColor color -> vec4,颜色被转换为线性,假设 QML 中指定的颜色值为 sRGB 空间。内置的 Qt 颜色(如 )也采用 sRGB 色彩空间,DefaultMaterial 和 的所有颜色属性都会执行相同的转换,因此 Effect 的这种行为与这些颜色相匹配。
"green"
PrincipledMaterial - 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,具体取决于 的纹理属性中使用的是 还是 。将 属性设置为 false 会导致着色器暴露出一个虚假纹理,这意味着着色器仍能正常工作,但会对含有不透明黑色图像内容的纹理进行采样。请注意,采样器的属性必须始终引用 对象,而不是直接引用 。说到 属性,只有源、平铺和滤波相关属性会与特效隐式结合,其余属性(如 UV 变换)则由自定义着色器根据自己的需要来实现。TextureInput Texture CubeMapTexture enabled TextureInput Texture Texture
注意: 如果着色器代码中引用的统一没有相应的属性,那么在运行时处理效果时将会导致着色器编译错误。也有一些例外情况,如采样器制服,当没有相应的 QML 属性时,会绑定一个虚拟纹理,但一般来说,所有制服和采样器都必须在效果对象中声明相应的属性。
开始使用用户定义的效果
自定义后处理特效至少需要一个特效对象和一个片段着色器片段。有些特效还需要自定义顶点着色器。
举个简单的例子,让我们创建一个效果,将场景内容与图像相结合,同时以动画方式进一步改变红色通道的值:
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
下访问。来自 quad 的 UV 坐标位于INPUT_UV
中。这些 UV 值始终适用于对INPUT
取样,与运行时的底层图形 API 无关(因此与图像中的 Y 轴方向无关,因为Qt Quick 3D 会自动进行必要的调整)。使用外部图像对纹理采样是通过TEXTURE_UV
来完成的。INPUT_UV
并不适合跨平台应用,因为需要翻转 V 以适应前面提到的坐标系差异,而基于图像的纹理和作为渲染目标的纹理使用的逻辑是不同的。幸运的是,所有这些都由引擎处理,因此着色器不需要进一步的逻辑。
一旦 simpleEffect 可用,就可以将其与View3D 的效果列表关联起来,SceneEnvironment :
environment: SceneEnvironment { effects: [ simpleEffect ] }
结果如下,左边是原始场景,右边是应用了效果的场景:
注: 着色器中的shader
属性值是一个 URL,与 QML 和Qt Quick 中的习惯做法一样,引用的是包含着色器片段的文件,其工作原理与ShaderEffect 或Image.source 非常相似。仅支持file
和qrc
方案。也可以省略file
方案,以方便指定相对路径。这样的路径会相对于组件(.qml
文件)的位置进行解析。
注: 着色器代码始终使用 Vulkan 风格的 GLSL 提供,与 Qt 在运行时使用的图形 API 无关。
注: 效果提供的顶点和片段着色器代码本身并不是完整的 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; 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); } |
效果对象的passes
列表现在应同时指定顶点和片段:
passes: Pass { shaders: [ Shader { stage: Shader.Vertex shader: "effect.vert" }, Shader { stage: Shader.Fragment shader: "effect.frag" } ] }
最终结果如下:
效果着色器中的特殊关键字
VARYING
- 根据当前着色器的类型,声明顶点输出或片段输入。MAIN
- 此函数必须始终出现在效果着色器中。FRAGCOLOR
- - 最终片段颜色;片段着色器的输出。(仅限片段着色器)vec4
POSITION
- - 在顶点着色器中计算出的同质位置。(仅限顶点着色器)vec4
MODELVIEWPROJECTION_MATRIX
- - 屏幕四边形的变换矩阵。mat4
VERTEX
- - 四边形的顶点;顶点着色器的输入。(仅限顶点着色器)vec3
INPUT
- 或 - 输入纹理的采样器,并将场景渲染到采样器中,除非某个通道通过 对象重定向其输入,在这种情况下, 指的是由 引用的附加色彩缓冲区纹理。启用sampler2D
sampler2DArray
BufferInputINPUT
BufferInput多视图渲染(可能与 VR/AR 应用程序有关)后,这是一个 sampler2DArray,而输入纹理则成为一个 2D 纹理数组。INPUT_UV
- - 用于采样的 UV 坐标 。vec2
INPUT
TEXTURE_UV
- - 适用于从图像文件中加载内容的纹理采样的 UV 坐标。vec2
INPUT_SIZE
- - 纹理的大小(像素)。vec2
INPUT
OUTPUT_SIZE
- - 输出缓冲区的大小(像素)。通常与 相同,除非该通道输出到一个额外的缓冲区,并在其上设置了尺寸乘数。vec2
INPUT_SIZE
FRAME
- - 帧计数器,在 的每一帧后递增。float
View3DDEPTH_TEXTURE
- - 深度纹理,包含场景中不透明物体的深度缓冲区内容。与 一样,着色器中出现此关键字会自动生成深度纹理。sampler2D
CustomMaterialVIEW_INDEX
- - 启用uint
多视图渲染时,这是当前视图索引,在顶点和片段着色器中都可用。不使用多视图渲染时,该值始终为 0。
构建多通道效果
多通道效果通常会使用多组着色器,并使用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 }
如果所需值与默认值一致,则不必使用注释属性。
从内部来看,由于存在缓冲区对象,并从通道的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 的大小(在没有窗口系统的嵌入式设备上,其大小可能与屏幕分辨率一样大)。多通道特效以及应用多种特效会进一步增加资源和性能需求。
因此,最好在开发生命周期的早期就确保目标设备和 Graphical 栈能够在最终产品的屏幕分辨率下应对 3D 场景设计中包含的特效。
虽然对于需要它的技术来说,DEPTH_TEXTURE
是不可避免的,但这意味着需要额外的渲染过程来生成纹理的内容,这也会对性能较差的硬件造成影响。因此,只有在必要时才在特效着色器中使用DEPTH_TEXTURE
。
着色器中操作的复杂程度也很重要。与CustomMaterial 一样,如果片段着色器不够理想,很容易导致渲染性能降低。
当涉及的数值大于 1 时,请谨慎使用sizeMultiplier in Buffer 。例如,乘数为 4 意味着创建并渲染的纹理大小是View3D 的 4 倍。就像阴影贴图和多重或超采样一样,在 GPU 能力有限的系统上,增加的资源和性能成本很快就会超过更高质量带来的好处。
虚拟现实/增强现实的注意事项
使用Qt Quick 3D XR 为虚拟现实或增强现实开发应用程序时,后处理效果是可用的。但是,设计人员和开发人员应特别注意了解哪些特效和哪种特效在虚拟现实环境中有意义。某些特效,包括ExtendedSceneEnvironment 中的某些内置特效或已废弃的特效模块,在虚拟现实环境中不会带来良好的视觉体验,甚至可能影响用户的身体健康(例如导致晕动病或头晕)。
当在 VR/AR 应用程序中启用更高效的多视图渲染模式时,左右眼的内容不会单独进行渲染。取而代之的是,所有渲染都在一次渲染中完成,使用的是具有两个层的二维纹理阵列,而不是两个独立的二维纹理。这也意味着在这种模式下,许多中间缓冲区(指色彩或深度纹理)需要变成纹理阵列。这将对自定义材质和后处理效果产生影响。输入纹理 (INPUT
)、深度纹理 (DEPTH_TEXTURE
)、屏幕纹理 (SCREEN_TEXTURE
)等纹理会变成二维纹理数组,在着色器中以sampler2DArray
而不是sampler2D
的形式显示。这对 GLSL 函数(如 texture()、textureLod() 或 textureSize())有影响。此时 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
另请参阅 Shader,Pass,Buffer,BufferInput 和Qt Quick 3D - 自定义效果示例。
属性文档
© 2025 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.