CustomMaterial QML 类型

创建自定义着色器的基础组件,用于着色模型。 更多信息...

导入声明import QtQuick3D
继承自

Material

属性

详细说明

自定义着色器允许使用自定义着色器代码进行着色,从而在图形着色器级别实现可编程性。可以提供顶点、片段或两者都的着色器。`vertexShader`和`fragmentShader`属性是URL,引用包含着色器代码片段的文件,并且工作方式与`ShaderEffect`或`Image.source`非常相似。自定义着色器只支持`file`和`qrc`方案。还可以省略`file`方案,以方便地指定相对路径。此类路径相对于组件的位置(`.qml`文件)解析。

有关自定义着色器的入门指南,请参阅页面《可编程材料、效果、几何体和纹理数据》。

简介

考虑以下相同场景的不同版本。在左侧,圆柱体使用内置的非可编程材料。此类材料可以通过大量属性进行配置,但无法进一步控制底层生成的着色器。在右侧,相同的圆柱体现在关联到引用应用程序提供的顶点和片段着色器代码片段的自定义材料。这允许在顶点着色器中插入自定义的、特定于应用程序的逻辑以转换几何体,并在片段着色器中以自定义方式确定某些颜色属性。作为着色的自定义材料,圆柱体仍然按照场景照明方式正常参与。

View3D {
    anchors.fill: parent
    PerspectiveCamera {
        id: camera
        position: Qt.vector3d(0, 0, 600)
    }
    camera: camera
    DirectionalLight {
        position: Qt.vector3d(-500, 500, -100)
        color: Qt.rgba(0.2, 0.2, 0.2, 1.0)
        ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
    }
    Model {
        source: "#Cylinder"
        eulerRotation: Qt.vector3d(30, 30, 0)
        scale: Qt.vector3d(1.5, 1.5, 1.5)
        materials: [
            DefaultMaterial {
                diffuseColor: Qt.rgba(0, 1, 0, 1)
            }
        ]
    }
}
View3D {
    anchors.fill: parent
    PerspectiveCamera {
        id: camera
        position: Qt.vector3d(0, 0, 600)
    }
    camera: camera
    DirectionalLight {
        position: Qt.vector3d(-500, 500, -100)
        color: Qt.rgba(0.2, 0.2, 0.2, 1.0)
        ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
    }
    Model {
        source: "#Cylinder"
        eulerRotation: Qt.vector3d(30, 30, 0)
        scale: Qt.vector3d(1.5, 1.5, 1.5)
        materials: [
            CustomMaterial {
                vertexShader: "material.vert"
                fragmentShader: "material.frag"
                property real uTime
                property real uAmplitude: 50
                NumberAnimation on uTime { from: 0; to: 100; duration: 10000; loops: -1 }
            }
        ]
    }
}

假设在 material.vertmaterial.frag 中的着色器片段如下

void MAIN()
{
    VERTEX.x += sin(uTime + VERTEX.y) * uAmplitude;
}
void MAIN()
{
    BASE_COLOR = vec4(0.0, 1.0, 0.0, 1.0);
}

注意 uTimeuAmplitude 是 CustomMaterial 元素的属性。它们可以改变值并且被正常动画化,这些值将自动暴露给着色器,无需开发者进行任何进一步的操作。

结果是渲染出可以动画其顶点的圆柱形

自定义材料有两种方式

主要有两种自定义材料。这由 shadingMode 属性指定。在 unshaded 自定义材料中,片段着色器输出一个单独的 vec4 颜色,忽略场景中的光源、光照探针和阴影。在 shaded 材料中,预期着色器要实现某些函数并使用内置变量来考虑光照和阴影的贡献。

默认选择通常是着色材料,这体现在 shadingMode 属性的默认值上。这适用于需要变换顶点或其他几何数据,或以自定义方式确定值,如 BASE_COLOREMISSIVE_COLOR 的材料,可能通过采样 SCREEN_TEXTUREDEPTH_TEXTURE,同时仍然接收场景中的光照和阴影贡献。此外,此类材料还可以覆盖和重新实现用于计算方向光、点光等光源的贡献的计算方程序。在幕后,Qt Quick 3D 引擎会大幅修改应用程序提供的着色器片段,以便提供标准材料所具有的功能,如光照。

当对象的形状完全由自定义着色器代码确定时,无着色材料很有用。对于此类材料的着色器,引擎添加的最少,因此最终片段颜色完全由着色器决定。这提供了更多的自由度,但也限制了与场景中其他元素(如灯光)集成的能力。

注意:着色器代码总是使用 Vulkan 风格的 GLSL 提供,无论运行时 Qt 使用的图形 API 是什么。

注意:材料提供的顶点和片段着色器代码本身不是完整的、完整的 GLSL 着色器。相反,它们提供了一套函数,然后由引擎进一步添加着色器代码。

向着色器暴露数据

可以使用 QML 和 Qt Quick 功能更改和动画化 CustomMaterial 的动态属性,并且这些值会自动暴露给着色器。在实践中,这与 ShaderEffect 非常相似。以下列表显示了属性是如何映射的

  • bool, int, real -> bool, int, float
  • QColor, color -> vec4,并将颜色转换为线性,假设 QML 中指定的颜色值为 sRGB 空间。内置的 Qt 颜色,如 "green" 也在 sRGB 颜色空间中,对所有 DefaultMaterial 和 PrincipledMaterial 的颜色属性执行相同的转换,因此 CustomMaterial 的这种行为与之匹配。与 Qt Quick 不同,对于 Qt Quick 3D,线性化是基本的,因为通常会对 3D 场景执行色调映射。
  • QRect, QRectF, rect -> vec4
  • QPointQPointFpointQSizeQSizeFsize -> vec2
  • QVector2Dvector2d -> vec2
  • QVector3Dvector3d -> vec3
  • QVector4Dvector4d -> vec4
  • QMatrix4x4matrix4x4 -> mat4
  • QQuaternionquaternion -> vec4,标量值是 w
  • TextureInput -> sampler2D 或 samplerCube,具体取决于在 TextureInput 的纹理属性中使用的是 Texture 还是 CubeMapTexture。将 enabled 属性设置为 false 将导致向着色器暴露一个虚拟纹理,这意味着着色器仍然可用,但会采样一个具有不透明黑色图像内容的纹理。注意,采样器的属性必须始终引用一个 TextureInput 对象,而不是直接引用 Texture。对于 Texture 属性,只有与源、平铺和过滤相关的属性在自定义材料中隐式考虑,因为其余的(例如 UV 变换)则取决于自定义着色器按照需要实现。

注意:当在着色器代码中引用的不变属性没有相应的属性时,在运行时处理材料时将导致着色器编译错误。有一些例外,例如采样器不变属性,在没有对应的 QML 属性时绑定一个虚拟纹理,但通常规则是,所有不变属性和采样器都必须在 CustomMaterial 对象中声明对应的属性。

不着色自定义材料

以下是一个 不着色 自定义材料的示例。

CustomMaterial {
    // These properties are automatically exposed to the shaders
    property real time: 0.0
    property real amplitude: 5.0
    property real alpha: 1.0
    property TextureInput tex: TextureInput {
        enabled: true
        texture: Texture { source: "image.png" }
    }

    shadingMode: CustomMaterial.Unshaded
    sourceBlend: alpha < 1.0 ? CustomMaterial.SrcAlpha : CustomMaterial.NoBlend
    destinationBlend: alpha < 1.0 ? CustomMaterial.OneMinusSrcAlpha : CustomMaterial.NoBlend
    cullMode: CustomMaterial.BackFaceCulling

    vertexShader: "customshader.vert"
    fragmentShader: "customshader.frag"
}

在上面的示例中,不着色 顶点和片段着色器片段可能如下所示。注意着色器不会也不会声明不变量或顶点输入,因为这是由 Qt 在组合最终着色器代码时处理的。

VARYING vec3 pos;
VARYING vec2 texcoord;

void MAIN()
{
    pos = VERTEX;
    pos.x += sin(time * 4.0 + pos.y) * amplitude;
    texcoord = UV0;
    POSITION = MODELVIEWPROJECTION_MATRIX * vec4(pos, 1.0);
}
VARYING vec3 pos;
VARYING vec2 texcoord;

void MAIN()
{
    vec4 c = texture(tex, texcoord);
    FRAGCOLOR = vec4(pos.x * 0.02, pos.y * 0.02, pos.z * 0.02, alpha) * c;
}

以下特殊的大写关键字可用

  • MAIN -> 顶点或片段着色器片段入口点的名称必须是 MAIN。在着色器片段中对不着色自定义材料提供此函数是强制性的。
  • VARYING -> 声明顶点着色器的输出或片段着色器的输入
  • POSITION -> vec4,顶点着色器的输出
  • FRAGCOLOR -> vec4,片段着色器的输出。仅适用于不着色自定义材料。
  • VERTEX -> vec3,顶点着色器中的顶点位置。
  • NORMAL -> vec3,顶点着色器中的顶点法线。如果关联的模型网格不提供法线,则值为 vec3(0.0)。
  • UV0 -> vec2,顶点着色器中的第一组纹理坐标。如果关联的模型网格不提供纹理坐标,则值为 vec2(0.0)。
  • UV1 -> vec2,顶点着色器中的第二组纹理坐标。如果关联的模型网格不提供第二组纹理坐标,则值为 vec2(0.0)。
  • 颜色 -> vec4,顶点着色器中的顶点颜色。当相关模型没有提供逐顶点颜色时,值为vec4(1.0)。
  • 切线 -> vec3,顶点着色器中的切线。当相关模型没有提供切线数据时,值为vec3(0.0)。
  • 副法线 -> vec3,顶点着色器中的副法线。当相关模型没有提供副法线数据时,值为vec3(0.0)。
  • 关节 -> ivec4,顶点着色器中的关节索引。当相关模型没有提供关节索引数据时,值为ivec4(0)。
  • 权重 -> vec4,顶点着色器中的关节权重。当相关模型没有提供关节权重数据时,值为vec4(0.0)。
  • MORPH_POSITION(n) -> vec3,顶点着色器中的第 n+1 个形态目标位置。相关模型应提供适当的数据。
  • MORPH_NORMAL(n) -> vec3,顶点着色器中的第 n+1 个形态目标法线。相关模型应提供适当的数据。
  • MORPH_TANGENT(n) -> vec3,顶点着色器中的第 n+1 个形态目标切线。相关模型应提供适当的数据。
  • MORPH_BINORMAL(n) -> vec3,顶点着色器中的第 n+1 个形态目标副法线。相关模型应提供适当的数据。
  • MODELVIEWPROJECTION_MATRIX -> mat4,模型-视图-投影矩阵。投影矩阵始终遵循OpenGL约定,包含对于Y轴方向和裁剪深度的烘焙式转换,具体取决于运行时使用的图形API。
  • VIEWPROJECTION_MATRIX -> mat4,视图-投影矩阵
  • PROJECTION_MATRIX -> mat4,投影矩阵
  • INVERSE_PROJECTION_MATRIX -> mat4,逆投影矩阵
  • VIEW_MATRIX -> mat4,视图(相机)矩阵
  • MODEL_MATRIX -> mat4,模型(世界)矩阵
  • NORMAL_MATRIX -> mat3,法线矩阵(模型矩阵左上角3x3部分的逆的转置)
  • BONE_TRANSFORMS -> mat4[],模型骨骼矩阵数组
  • BONE_NORMAL_TRANSFORMS -> mat3[],模型骨骼法线矩阵数组(每个骨骼矩阵左上角3x3部分的逆的转置)
  • MORPH_WEIGHTS -> float[],形态权重数组。相关模型应提供适当的数据。为了安全,QT_MORPH_MAX_COUNT 定义为此数组的长度。
  • CAMERA_POSITION -> vec3,世界空间中的相机位置
  • CAMERA_DIRECTION -> vec3,相机方向向量
  • CAMERA_PROPERTIES -> vec2,相机的近裁剪和远裁剪值
  • POINT_SIZE -> float,仅在顶点着色器中可写。当用顶点拓扑渲染几何体时,自定义顶点着色器必须将此设置为1.0或另一个值,这在着色和非着色自定义材料中都是如此。有关对其他大小支持的说明,请参阅PrincipledMaterial::pointSize

着色自定义材料

一个 着色 材料通过增加由 PrincipledMaterial 生成的着色器代码。与提供几乎所有顶点和片元着色器主函数逻辑的未着色材料不同,着色材料允许着色器生成过程正常进行,就像自定义材料是一个 PrincipledMaterial。期望顶点和片元着色器片段提供可选函数,然后在这些函数的特定点上调用它们,这样它们就可以根据需要自定义颜色和其他值,这些值随后用于光照计算和最终的片元颜色。

而不是只实现一个 MAIN 函数,着色自定义材质的片段着色器可以实现多个函数。包括 MAIN 在内,所有函数在着色自定义材质中都是可选的。一个空的着色器代码片段,甚至不指定 vertexShaderfragmentShader 属性也是完全可以接受的。

着色自定义材料中的顶点着色器代码片段

以下函数可以实现在顶点着色器代码片段中

  • void MAIN() 如果存在,此函数将用于设置 POSITION 的值,这是从顶点着色器输出的 vec4,以及可选地修改 VERTEXCOLORNORMALUV0UV1TANGENTBINORMALJOINTSWEIGHTS 的值。与不着色材料不同,写入这些值是有意义的,因为修改后的值将在生成的其余着色器代码中被考虑(而不着色材料没有生成额外的着色器代码)。例如,如果自定义顶点着色器移动了顶点或法线,它将希望将修改后的值存储到 VERTEXNORMAL 中,以实现后续正确的光照计算。此外,该函数可以写入使用 VARYING 定义的变量,以便将插值数据传递给片段着色器。当此函数或 POSITION 的重新定义不存在时,POSITION 将基于 VERTEXMODELVIEWPROJECTION_MATRIX 进行计算,就像 PrincipledMaterial 所做的那样。

    示例:既依赖于作为统一变量暴露的 QML 属性,也向片段着色器传递数据

    VARYING vec3 vNormal;
    VARYING vec3 vViewVec;
    
    void MAIN()
    {
        VERTEX.x += sin(uTime * 4.0 + VERTEX.y) * uAmplitude;
        vNormal = normalize(NORMAL_MATRIX * NORMAL);
        vViewVec = CAMERA_POSITION - (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
        POSITION = MODELVIEWPROJECTION_MATRIX * vec4(VERTEX, 1.0);
    }

    注意:在上面的示例中,将值赋给 POSITION 是可选的,因为在此情况下的用法与默认行为相同。

着色自定义材料中的片段着色器代码片段

以下函数可以在片段着色器代码片段中实现

  • void MAIN() 如果存在,此函数将用于设置特殊可写变量 BASE_COLORMETALNESSROUGHNESSSPECULAR_AMOUNT、NORMAL 和 FRESNEL_POWER 的值。

    一个常见的用例是基于采样纹理设置 BASE_COLOR 的值,无论是基础颜色贴图、SCREEN_TEXTURE,还是其他类型的源。在没有实现自定义光照处理器函数的情况下,这特别相关且方便。将 BASE_COLOR.a 设置为非默认的 1.0 允许影响片段的最终 alpha 值。(注意,这通常还需要在 sourceBlenddestinationBlend 中启用 alpha 混合)

    另一个场景是没有提供自定义的 SPECULAR_LIGHT 函数,或者当在 SceneEnvironment 中设置了光照探头时。在 MAIN 中可以设置金属度、粗糙度和其他会影响镜面贡献计算的值,以实现所需的自定义值。

    该函数可以写入以下特殊变量。写入到这些变量的值通常是硬编码的或基于映射到统一变量的 QML 属性计算而来的。语义与 PrincipledMaterial 相同。

    • vec4 BASE_COLOR - 基础颜色和材料alpha值。对应于 内建材料的颜色属性。当未实现光线处理函数时,在 MAIN 中设置自定义基础颜色会方便,因为那是默认光线计算的考虑因素。默认值为 vec4(1.0),表示alpha为1.0的白色。alpha值影响片段的最终alpha值。最终的alpha值是对象(模型)不透明度乘以基础颜色alpha值。在着色器代码中直接指定值时,不依赖从QML的 color 属性暴露的统一值,请注意,如果需要,着色器必须执行sRGB到线性的转换。例如,假设有 vec3 colorfloat alpha,这可以按以下方式实现
      float C1 = 0.305306011;
      vec3 C2 = vec3(0.682171111, 0.682171111, 0.682171111);
      vec3 C3 = vec3(0.012522878, 0.012522878, 0.012522878);
      BASE_COLOR = vec4(rgb * (rgb * (rgb * C1 + C2) + C3), alpha);
    • vec3 EMISSIVE_COLOR - 自发光颜色。对应于内建材料的自发光颜色,该颜色通过 内建材料的自发光因子属性内建材料的光照图属性 进行组合。默认值为 vec3(0.0)。在着色器代码中直接指定值时,不依赖从QML的 color 属性暴露的统一值,请注意,如果需要,着色器必须执行sRGB到线性的转换。
    • float METALNESS 金属量,范围为0.0 - 1.0。默认值为0。必须设置非零值才有作用。
    • float ROUGHNESS 粗糙度值,范围为0.0 - 1.0。默认值为0。
    • float FRESNEL_POWER 指定Fresnel功率。典型值,也是默认值,是 5.0,因为PrincipledMaterial就这样使用。
    • float SPECULAR_AMOUNT 镜面值,范围为0.0 - 1.0。默认值为 0.5,匹配PrincipledMaterial::specularAmount。必须设置非零值才有作用。
    • vec3 NORMAL - 顶点着色器在世界空间中提供的法线。虽然此属性与VAR_WORLD_NORMAL具有相同的初始值,但只有改变NORMAL的值才会影响光照。
    • vec3 TANGENT - 顶点着色器在世界空间中提供的切向。此值可能已经调整以适应双面。
    • vec3 BINORMAL - 顶点着色器在世界空间中提供的双法线。此值可能已经调整以适应双面。
    • vec2 UV0 - 来自顶点着色器的第一组纹理坐标。在此属性在片段着色器中是只读的。
    • vec2 UV1 - 来自顶点着色器的第二组纹理坐标。在此属性在片段着色器中是只读的。

    注意:与无着色材料不同,有着色材料的片段 MAIN 并没有直接控制 FRAGCOLOR。相反,是光线处理函数中写入的 DIFFUSESPECULAR 值决定了最终的片段颜色。当没有实现光线处理函数时,相关的默认着色计算将像 PrincipledMaterial 一样执行,考虑BASE_COLOR和其他上面的列表值。

    一个简单金属自定材料着色器的示例可能是以下内容

    void MAIN()
    {
        METALNESS = 1.0;
        ROUGHNESS = 0.5;
        FRESNEL_POWER = 5.0;
    }

    另一个示例,其中基础颜色和alpha通过采样纹理设置

    VARYING vec2 texcoord;
    void MAIN()
    {
        BASE_COLOR = texture(uColorMap, texcoord);
    }
  • void AMBIENT_LIGHT() 当存在时,此函数会对每个片段调用一次。函数的任务是将总环境贡献添加到一个可写特殊变量 DIFFUSE 中。当然可以选择计算不同的值,或者根本不修改 DIFFUSE (以忽略全部环境光)。当这个函数根本不存在时,环境贡献会像 PrincipledMaterial 一样正常计算。

    函数可以写入以下特殊变量

    • vec3 DIFFUSE 对每个片段累加漫反射光线贡献。光照处理函数通常会向其添加(+=),因为如果覆盖其将丢失来自其他光线的贡献。

    函数可以读取以下特殊变量,包括表格上方(例如,MODEL_MATRIX)和向量(例如,CAMERA_POSITION)常量

    • vec3 TOTAL_AMBIENT_COLOR 场景中的总环境贡献。

    示例

    void AMBIENT_LIGHT()
    {
        DIFFUSE += TOTAL_AMBIENT_COLOR;
    }
  • void DIRECTIONAL_LIGHT() 当存在时,此函数会对场景中每个活动的方向光对每个片段调用。函数的任务是将漫反射贡献添加到一个可写特殊变量 DIFFUSE 中。函数也可以选择什么也不做,此时会忽略方向光线的漫反射贡献。当这个函数根本不存在时,方向光线的漫反射贡献会像 PrincipledMaterial 一样正常累加。

    函数可以写入以下特殊变量

    • vec3 DIFFUSE 对每个片段累加漫反射光线贡献。光照处理函数通常会向其添加(+=),因为如果覆盖其将丢失来自其他光线的贡献。

    函数可以读取以下特殊变量,包括表格上方(例如,MODEL_MATRIX)和向量(例如,CAMERA_POSITION)常量

    • vec3 LIGHT_COLOR 漫反射光颜色。
    • float SHADOW_CONTRIB 影子贡献,如果没有影子或未接收影子时为 1.0。
    • vec3 TO_LIGHT_DIR 指向光源的向量。
    • vec3 NORMAL 世界上面的法线向量。
    • vec4 BASE_COLOR 基础颜色和材质 alpha 值。
    • float METALNESS 金属度。
    • float ROUGHNESS 粗糙度。

    示例

    void DIRECTIONAL_LIGHT()
    {
        DIFFUSE += LIGHT_COLOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
    }
  • void POINT_LIGHT() 当存在时,此函数会对场景中每个活动的点光对每个片段调用。函数的任务是将漫反射贡献添加到一个可写特殊变量 DIFFUSE 中。函数也可以选择什么也不做,此时会忽略点光线的漫反射贡献。当这个函数根本不存在时,点光线的漫反射贡献会像 PrincipledMaterial 一样正常累加。

    函数可以写入以下特殊变量

    • vec3 DIFFUSE 对每个片段累加漫反射光线贡献。

    函数可以读取以下特殊变量,包括表格上方(例如,MODEL_MATRIX)和向量(例如,CAMERA_POSITION)常量

    • vec3 LIGHT_COLOR 漫反射光颜色。
    • float LIGHT_ATTENUATION 光线衰减。
    • float SHADOW_CONTRIB 影子贡献,如果没有影子或未接收影子时为 1.0。
    • vec3 TO_LIGHT_DIR 指向光源的向量。
    • vec3 NORMAL 世界上面的法线向量。
    • vec4 BASE_COLOR 基础颜色和材质 alpha 值。
    • float METALNESS 金属度。
    • float ROUGHNESS 粗糙度。

    示例

    void POINT_LIGHT()
    {
        DIFFUSE += LIGHT_COLOR * LIGHT_ATTENUATION * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
    }
  • void SPOT_LIGHT() 当存在时,此函数会对场景中每个活动的聚光灯对每个片段调用。函数的任务是将漫反射贡献添加到一个可写特殊变量 DIFFUSE 中。函数也可以选择什么也不做,此时会忽略聚光灯的漫反射贡献。当这个函数根本不存在时,聚光灯的漫反射贡献会像 PrincipledMaterial 一样正常累加。

    函数可以写入以下特殊变量

    • vec3 DIFFUSE 对每个片段累加漫反射光线贡献。

    函数可以读取以下特殊变量,包括表格上方(例如,MODEL_MATRIX)和向量(例如,CAMERA_POSITION)常量

    • vec3 LIGHT_COLOR 漫反射光颜色。
    • float LIGHT_ATTENUATION 光线衰减。
    • float SHADOW_CONTRIB 影子贡献,如果没有影子或未接收影子时为 1.0。
    • vec3 TO_LIGHT_DIR 指向光源的向量。
    • float SPOT_FACTOR 聚光灯因子。
    • vec3 NORMAL 世界上面的法线向量。
    • vec4 BASE_COLOR 基础颜色和材质 alpha 值。
    • float METALNESS 金属度。
    • float ROUGHNESS 粗糙度。

    示例

    void SPOT_LIGHT()
    {
        DIFFUSE += LIGHT_COLOR * LIGHT_ATTENUATION * SPOT_FACTOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
    }
  • void SPECULAR_LIGHT() 当存在时,此函数会对场景中每个活动的光对每个片段调用。函数的任务是将镜面贡献添加到一个可写特殊变量 SPECULAR 中。函数也可以选择什么也不做,此时会忽略光的镜面贡献。当这个函数根本不存在时,光影贡献会像 PrincipledMaterial 一样正常累加。

    函数可以写入以下特殊变量

    • vec3 SPECULAR 累加每个碎片的镜面光贡献。光线处理函数通常会通过 (+=) 向其添加,因为覆盖值将丢失来自其他光线的贡献。

    函数可以读取以下特殊变量,包括表格上方(例如,MODEL_MATRIX)和向量(例如,CAMERA_POSITION)常量

    • vec3 LIGHT_COLOR 镜面光的颜色。
    • float LIGHT_ATTENUATION 光衰减。对于定向光,该值为1.0。对于聚光灯,该值与void SPOT_LIGHTLIGHT_ATTENUATION * SPOT_FACTOR相同。
    • float SHADOW_CONTRIB 影子贡献,如果没有影子或未接收影子时为 1.0。
    • vec3 FRESNEL_CONTRIB 内置Fresnel计算的Fresnel贡献。
    • vec3 TO_LIGHT_DIR 指向光源的向量。
    • vec3 NORMAL 世界上面的法线向量。
    • vec4 BASE_COLOR 基础颜色和材质 alpha 值。
    • float METALNESS 金属度。
    • float ROUGHNESS 粗糙度。
    • float SPECULAR_AMOUNT 镜面量。此值将在0.0到1.0之间,将与在自定义MAIN函数中设置的相同值相同。当不使用FRESNEL_CONTRIB提供的内置Fresnel贡献时,此值对于计算Fresnel贡献将很有用。
    void SPECULAR_LIGHT()
    {
        vec3 H = normalize(VIEW_VECTOR + TO_LIGHT_DIR);
        float cosAlpha = max(0.0, dot(H, normalize(NORMAL)));
        float shine = pow(cosAlpha, exp2(15.0 * (1.0 - ROUGHNESS) + 1.0) * 0.25);
        SPECULAR += shine * LIGHT_COLOR * FRESNEL_CONTRIB * SHADOW_CONTRIB * LIGHT_ATTENUATION;
    }
  • void POST_PROCESS) 当存在时,此函数在片段管线末尾被调用。该函数的任务是使用最终的漫反射、镜面和辐射项最终化COLOR_SUM。与无阴影材料的FRAGCOLOR不同,COLOR_SUM将在写入帧缓冲区之前自动调色映射。为了调试目的,有时需要输出一个不应被视为颜色的值。为了避免调色映射扭曲此值,可以通过将tonemapMode 属性设置为 TonemapModeNone 来禁用此功能。

    函数可以写入以下特殊变量

    • vec4 COLOR_SUM 片段着色器的输出。默认值为 vec4(DIFFUSE.rgb + SPECULAR + EMISSIVE, DIFFUSE.a)

    函数可以读取以下特殊变量。

    • vec4 DIFFUSE 片段管线中的最终漫反射项。
    • vec3 SPECULAR 片段管线中的最终镜面项。
    • vec3 EMISSIVE 片段管线中的最终辐射项。
    • vec2 UV0 - 着色器中的第一组纹理坐标。
    • vec2 UV1 - 着色器中的第二组纹理坐标。
    void POST_PROCESS()
    {
        float center_x = textureSize(SCREEN_TEXTURE, 0).x * 0.5;
        if (gl_FragCoord.x > center_x)
            COLOR_SUM = DIFFUSE;
        else
            COLOR_SUM = vec4(EMISSIVE, DIFFUSE.a);
    }
  • void IBL_PROBE() 当存在时,此函数会被调用以执行基于图像的光照 (IBL)。该函数的任务是将IBL的漫反射和镜面贡献添加到可写特殊变量 DIFFUSESPECULAR

    函数可以写入以下特殊变量

    • vec3 DIFFUSE 对每个片段累加漫反射光线贡献。
    • vec3 SPECULAR 累加每个碎片的镜面光贡献。

    函数可以读取以下特殊变量。

    void IBL_PROBE()
    {
        vec3 smpDir = IBL_ORIENTATION * NORMAL;
        DIFFUSE += AO_FACTOR * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, smpDir, IBL_MAXMIPMAP).rgb;
    }

函数之间的自定义变量

可以从MAIN函数向其他函数提供附加变量。可以使用SHARED_VARS关键字定义新自定义变量。这些用户定义的变量可以通过SHARED.访问。

例如,一个阴影自定义材质可以在MAIN中获取共享值并在其他函数中使用它。

SHARED_VARS {
    vec3 colorThreshold;
};
void MAIN()
{
    BASE_COLOR = texture(baseColorMap, UV0);
    SHARED.colorThreshold = texture(thresholdMap, UV0).rgb;
}
void DIRECTIONAL_LIGHT()
{
    if (DIFFUSE >= SHARED.colorThreshold) {
        DIFFUSE = SHARED.colorThreshold;
        return;
    }
    DIFFUSE += LIGHT_COLOR * SHADOW_CONTRIB;
}

注意:SHARED可以在所有函数中使用,而无需POST_PROCESS,但是安全地将其写入MAIN且在读写其他函数时是安全的。

注意:最佳用法是将SHARED写入LIGHT函数,首先在MAIN中重置它,然后在每个LIGHT函数中累加它。

SHARED_VARS {
    float sheenIntensity;
    float sheenRoughness;
    vec3 sheenColor;
    vec3 outSheenColor;
};
void MAIN()
{
    ...
    vec4 tex = texture(uSheenMap, UV0);
    SHARED.sheenColor = tex.rgb;
    SHARED.sheenIntensity = tex.a;
    SHARED.sheenRoughness = uSheenRoughness;
    SHARED.outSheenColor = vec3(0.0);
}
void SPECULAR_LIGHT()
{
    SHARED.outSheenColor += ...;
}
void POST_PROCESS()
{
    COLOR_SUM = DIFFUSE + SPECULAR + EMISSIVE + SHARED.outSheenColor;
}

注意:MAIN在所有其他函数之前调用,POST_PROCESS在所有其他函数之后调用,但是对于光处理器的其他顺序没有任何保证。

附加特殊关键字

自定义片段着色器代码可以自由访问统一变量(例如,CAMERA_DIRECTIONCAMERA_POSITION),并且可以访问从自定义顶点着色器传递过来的变量。此外,还有很多内置变量作为特殊关键字可用。其中一些是可选的,因为顶点 MAIN 可以自行计算并传递这些值,但为了减少数据重复,片段着色器也可以依赖于这些内置变量。这些内置变量在光照处理函数和片段 MAIN 中都可用。

  • vec3 VAR_WORLD_NORMAL - 由 NORMAL_MATRIX 变换后的插值法线。
  • vec3 VAR_WORLD_TANGENT - 由 MODEL_MATRIX 变换后的插值切线。
  • vec3 VAR_WORLD_BINORMAL - 由 MODEL_MATRIX 变换后的插值副法线。
  • vec3 NORMAL - 与 VAR_WORLD_NORMAL 不同,它不是直接插值法线,此值可能会根据双面性进行调整:在禁用裁剪渲染时,法线会根据需要进行反转。因此,建议在所有裁剪模式下使用 NORMAL 而不是 VAR_WORLD_NORMAL 来正确进行光照和其他计算。
  • vec3 TANGENT - 与 NORMAL 类似,此值可能会根据双面性进行调整:在禁用裁剪渲染时,切线会根据需要进行反转。
  • vec3 BINORMAL - 与 NORMAL 类似,此值可能会根据双面性进行调整:在禁用裁剪渲染时,副法线会根据需要进行反转。
  • vec3 VAR_WORLD_POSITION - 插值后的世界空间顶点位置((MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz
  • vec4 VAR_COLOR - 当在网格中提供颜色时,为插值后的顶点颜色。否则为 vec4(1.0)
  • vec3 VIEW_VECTOR - 指向相机。这实际上是 CAMERA_POSITION - VAR_WORLD_POSITION 向量归一化。
  • vec4 FRAGCOORD - 包含当前片段的窗口相关坐标。
  • float FRAMEBUFFER_Y_UP - 当帧缓冲区(纹理)的坐标系中 Y 轴向上时,此值为 1,意味着 (0, 0) 是左下角。当 Y 轴向下时,此值为 -1(0, 0) 是左上角。这种底层图形 API 的差异并不影响大多数自定义材料。一个值得注意的例外是使用 SCREEN_TEXTURE 并非基于 FRAGCOORD 的纹理坐标进行采样。由于 SCREEN_TEXTURE 的方向本质上是与底层图形 API 相关联的,因此可能需要对 Y 坐标进行适当的调整。

    例如,以下适用于矩形或立方体网格的片段着色器,将显示场景中的不透明对象在模型上。

    VARYING vec2 texcoord;
    void MAIN()
    {
        vec2 screencoord = texcoord;
        if (FRAMEBUFFER_Y_UP < 0.0) // effectively: if not OpenGL
            screencoord.y = 1.0 - screencoord.y;
        BASE_COLOR = texture(SCREEN_TEXTURE, screencoord);
    }

    在采样除 SCREEN_TEXTUREDEPTH_TEXTURE 之外的纹理,或者使用 FRAGCOORD 计算纹理坐标(通常用于访问屏幕和深度纹理)时,这种调整是不必要的。

  • float NDC_Y_UP - 当归一化设备坐标空间中的 Y 轴向上时,此值为 1,当 Y 轴向下时为 -1。Y 轴向下是在使用 Vulkan 渲染时的情况。大多数材料不需要考虑这一点,但在某些高级用例中,能够根据这一点进行分支变得非常有用。
  • 浮点数 NEAR_CLIP_VALUE - 当裁剪平面范围从 -1 开始到 1 结束时,此值为 -1。在 OpenGL 绘制中这确实是正确的。对于其他渲染后端,此属性的值将为 0,表示裁剪平面范围是 01。此值在某些涉及 DEPTH_TEXTURE 的技术中很有用。

    例如,以下片段着色器演示了一种从深度缓冲区重新构造值的位置的技术,以确定从当前渲染的位置的距离。当与 INVERSE_PROJECTION_MATRIX 结合使用时,深度值需要位于标准化设备坐标系中,因此重要的是确保深度值的范围反映了这一点。当 NEAR_CLIP_VALUE-1 时,深度值将按比例缩放到 -11 之间。

    void MAIN() {
        vec2 screen_uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0));
        float depth = texture(DEPTH_TEXTURE, screen_uv).r;
    
        if (NEAR_CLIP_VALUE < 0.0) // effectively: if opengl
            depth = depth * 2.0 - 1.0;
    
        vec4 unproject = INVERSE_PROJECTION_MATRIX * vec4(screen_uv, depth, 1.0);
        depth = (unproject.xyz / unproject.w).z;
        float viewVectorZ = (VIEW_MATRIX * vec4(VAR_WORLD_POSITION, 1.0)).z;
        depth = viewVectorZ - depth;
    
        BASE_COLOR = vec4(depth, depth, depth, 1.0);
    }
  • 浮点数 IBL_EXPOSE - 光探针发出的光量。来自 SceneEnvironment::probeExposure
    DIFFUSE += AO_FACTOR * IBL_EXPOSE * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
  • 浮点数 IBL_HORIZON - 来自下半部分环境的反射的水平截止值。来自 Horizon Cut-Off,但重映射到 [-1, 0)。
    vec3 diffuse += AO_FACTOR * IBL_EXPOSE * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
    if (IBL_HORIZON > -1.0) {
        float ctr = 0.5 + 0.5 * IBL_HORIZON;
        float vertWt = smoothstep(ctr * 0.25, ctr + 0.25, NORMAL.y);
        float wtScaled = mix(1.0, vertWt, IBL_HORIZON + 1.0);
        diffuse *= wtScaled;
    }
  • 浮点数 IBL_MAXMIPMAP - IBL_TEXTE的最大Mipmap级别。

实例化

在执行实例渲染时,一些上述关键字不适用。以下关键字仅适用于实例化

  • INSTANCE_MODEL_MATRIX -> mat4,用于替换 MODEL_MATRIX,包括实例化变换。
  • INSTANCE_MODELVIEWPROJECTION_MATRIX -> mat4,用于替换 MODELVIEWPROJECTION_MATRIX,包括实例化变换。
  • INSTANCE_COLOR -> vec4,实例颜色:要与 COLOR 结合。
  • INSTANCE_DATA -> vec4,实例自定义数据。
  • INSTANCE_INDEX -> int,实例编号,实例化表中的索引。

屏幕、深度和其他纹理

渲染管线可以暴露许多特殊渲染传递的内容来给自定义材质着色器,这既适用于着色又适用于不着色的自定义材质。

例如,一个着色器可能需要访问一个包含场景中不透明物体深度缓冲区内容的深度纹理。这是通过采样 DEPTH_TEXTURE 实现的。此类纹理通常不会生成,除非确实需要它。因此,以下关键字在顶点或片段着色器中也起到了打开或关闭 - 可能昂贵的 - 为生成特定纹理的传递的选项。当然,其中一些可能因其他设置而已启用,如 SceneEnvironment 中的环境光遮蔽参数,或依赖于深度纹理的后处理效果,在这种情况下,相关纹理将生成,无论自定义材质如何,则在材质中采样这些特殊纹理无需承担额外成本,除了纹理访问本身之外)

  • SCREEN_TEXTURE - 当存在时,包含渲染传递中的颜色缓冲区的纹理(sampler2D)排除任何透明材料或任何也使用 SCREEN_TEXTURE 的材料,以此名称暴露给着色器。此纹理可用于需要垂直框内容的技巧。SCREEN_TEXTURE 纹理使用与 View3D 相同的清除模式。这些纹理的大小与像素大小相匹配 View3D。例如,片段着色器可以包含以下内容
    vec2 uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0));
    vec2 displace = vec2(0.1);
    vec4 c = texture(SCREEN_TEXTURE, uv + displace);

    请注意,使用SCREEN_TEXTURE需要对场景进行适当的、有意识的设计。使用此类材料的对象必须谨慎放置,通常位于所有预期将在纹理中可见的其他对象之上。采用某种形式的半透明度的对象从不属于SCREEN_TEXTURE。通常SCREEN_TEXTURE将与MAIN中的BASE_COLOR结合使用。例如,以下自定义片段着色器应用凸出效果,同时保持所有未被不透明对象接触的片段透明。这假定具有材料的对象位于最前方,并且启用了混合。

    void MAIN()
    {
        vec2 size = vec2(textureSize(SCREEN_TEXTURE, 0));
        vec2 uv = FRAGCOORD.xy / size;
    
        // basic emboss effect
        vec2 d = vec2(1.0 / size.x, 1.0 / size.y);
        vec4 diff = texture(SCREEN_TEXTURE, uv + d) - texture(SCREEN_TEXTURE, uv - d);
        float c = (diff.x + diff.y + diff.z) + 0.5;
    
        float alpha = texture(SCREEN_TEXTURE, uv).a;
        BASE_COLOR = vec4(vec3(c), alpha);
    }
  • SCREEN_MIP_TEXTURE - 除在性能上可能因屏幕大小而在某种程度上具有高昂成本外,在大多数方面与SCREEN_TEXTURE相同,区别在于此纹理生成了米普图。因此,除非自定义材料通过技术依赖纹理米普级别(例如在着色器中使用textureLod),否则始终首选使用SCREEN_TEXTURE
  • DEPTH_TEXTURE - 存在时,一个包含(非线性化)深度缓冲区内容的纹理(sampler2D)将被命名为此名称暴露给着色器。仅包含不透明对象。例如,一个片段着色器可以包含以下内容
    ivec2 dtSize = textureSize(DEPTH_TEXTURE, 0);
    vec2 dtUV = (FRAGCOORD.xy) / vec2(dtSize);
    vec4 depthSample = texture(DEPTH_TEXTURE, dtUV);
    float zNear = CAMERA_PROPERTIES.x;
    float zFar = CAMERA_PROPERTIES.y;
    float zRange = zFar - zNear;
    float z_n = 2.0 * depthSample.r - 1.0;
    float d = 2.0 * zNear * zFar / (zFar + zNear - z_n * zRange);
    d /= zFar;
  • AO_TEXTURE - 如果存在且在SceneEnvironment中启用了屏幕空间环境遮蔽(意味着AO强度和距离均非零),则此名称下暴露了SSAO纹理(sampler2D)。采样此纹理在非着色材料中可能是有用的。着色材料具有内置的环境遮蔽支持。这意味着环境遮蔽因素会自动考虑。而在非着色材料的片段着色器中可以编写以下代码以实现相同效果
    ivec2 aoSize = textureSize(AO_TEXTURE, 0);
    vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
    float aoFactor = texture(AO_TEXTURE, aoUV).x;
  • IBL_TEXTURE - 它不会启用任何特殊的渲染流程,但可以在材料有Material::lightProbe或模型处于SceneEnvironment::lightProbe的作用域内时使用。
    void IBL_PROBE()
    {
        DIFFUSE += AO_FACTOR * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
    }

另请参阅SceneEnvironment::tonemapMode使用基于图像的照明Qt Quick 3D - 自定义着色器示例Qt Quick 3D - 自定义材料示例,以及可编程材料、效果、几何形状和纹理数据

属性文档

alwaysDirty : bool

指定材料状态始终为脏,这表明每次由QtQuick3D使用时都需要刷新。


destinationAlphaBlend : 枚举 [since 6.7]

指定目标alpha混合因子。默认值是CustomMaterial.NoBlend。只有在将sourceBlenddestinationBlend设置为非默认值时,此值才会被积极使用。

常量
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

注意:为了向后兼容,当设置为默认值时,将其赋予与 destinationBlend 相同的值,如果 sourceBlenddestinationBlend 被设置为非默认值。

此属性是在 Qt 6.7 中引入的。

另请参阅destinationBlend


destinationBlend : 枚举

指定目标混合系数。默认值是 CustomMaterial.NoBlend

常量
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

注意:在启用混合之前,sourceBlend 和 destinationBlend 都需要设置为非默认值。

另请参阅:sourceBlend


fragmentShader : url

指定包含自定义片元着色器代码片段的文件。

该值是一个 URL,必须是本地文件或使用 qrc 方案来访问通过 Qt 资源系统嵌入的文件。也接受相对文件路径(不带方案),在这种情况下,文件被视为相对于组件(即 .qml 文件)。

另请参阅:vertexShader


lineWidth : real

当几何体使用线或线段原语类型时,此属性决定线的宽度。默认值是 1.0。当渲染其他类型的几何体,如三角形网格时,此属性不相关。

警告:运行时可能不支持除 1 之外的其他线宽,这取决于底层图形 API。在这种情况下,忽略更改宽度的请求。例如,以下都不能支持宽线:Direct3D、Metal、带有核心配置上下文的 OpenGL。

注意:与线宽不同,点型拓扑的几何体的像素大小由顶点着色器(在支持的情况下)控制,因此没有相应的 QML 属性。


shadingMode : 枚举

指定材料类型。默认值是 Shaded。

常量
CustomMaterial.Unshaded
CustomMaterial.Shaded

sourceAlphaBlend : 枚举 [自 6.7]

指定源 alpha 混合系数。默认值是 CustomMaterial.NoBlend。只有在 sourceBlend 和 destinationBlend 被设置为非默认值时,此值才会被积极使用。

常量
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

注意:为了向后兼容,当设置为默认值时,在与 sourceBlend 和 destinationBlend 设置为非默认值时,将被赋与相同的值。

此属性是在 Qt 6.7 中引入的。

另请参阅:sourceBlend


sourceBlend : 枚举

指定源混合系数。默认值是 CustomMaterial.NoBlend

常量
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

注意:在启用混合之前,sourceBlend 和 destinationBlend 都需要设置为非默认值。

另请参阅destinationBlend


vertexShader : url

指定包含自定义顶点着色器代码片段的文件。

该值是一个 URL,必须是本地文件或使用 qrc 方案来访问通过 Qt 资源系统嵌入的文件。也接受相对文件路径(不带方案),在这种情况下,文件被视为相对于组件(即 .qml 文件)。

另请参阅fragmentShader


© 2024 The Qt Company Ltd. 本文档中所包含的文档贡献属于其各自的版权所有者。本文档根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款获得许可。Qt及其相关商标是The Qt Company Ltd.在芬兰以及全球其他国家的商标。所有其他商标均为其各自所有者的财产。