Qt Quick 3D - 自定义材质示例
演示如何编写带着色的自定义材质。

本示例展示了如何编写带着色效果的 custom materials 。使用带着色效果的材质时,我们无需编写完整的着色器程序,而是编写用于修改Qt标准着色器的函数。这样,生成的材质默认会参与光照计算、阴影映射,并兼容光探针。 我们只需针对需要特殊行为的情况编写自定义逻辑。这通过在顶点着色器和片段着色器的特定阶段调用自定义函数,从而有效扩展原本为PrincipledMaterial 生成的着色器代码来实现。
要创建带着色效果的自定义材质,请将shadingMode 属性设置为CustomMaterial.Shaded 。
一个简单的材质
第一个模型使用了一个简单的材质,该材质未添加任何自定义逻辑。我们像设置其他材质一样,将该自定义材质应用于模型:
Model {
source: "weirdShape.mesh"
scale: Qt.vector3d(100, 100, 100)
rotation: Quaternion.fromEulerAngles(-90, 0, 0)
x: v3d.radius
materials: [
CustomMaterial {
shadingMode: CustomMaterial.Shaded
fragmentShader: "material_simple.frag"
property color uDiffuse: "fuchsia"
property real uSpecular: 1.0
}
]
}除了设置 `shadingMode ` 和 `fragmentShader` 之外,我们还向材质中添加了两个属性:`uDiffuse ` 和 `Specular`。这些属性将被片段着色器捕获。
片段着色器的代码很简短:
void MAIN()
{
SPECULAR_AMOUNT = uSpecular;
BASE_COLOR = uDiffuse;
}所有着色器都必须实现MAIN 函数。在此函数中,我们使用材质中定义的属性来设置值,这些值将被Qt的标准着色器代码所使用。请注意,我们无需将这些属性声明为uniform:只需确保名称匹配即可。 如果材质中没有匹配的属性,将会引发着色器编译错误。
特殊变量SPECULAR_AMOUNT 和BASE_COLOR 分别对应PrincipledMaterial 中的specularAmount 和baseColor 。标准着色器代码会利用这些变量进行光照计算,其效果与使用PrincipledMaterial 完全一致。
自定义光源处理
下一个对象使用了一种更复杂的材质,该材质实现了自定义照明。虽然该材质的 uniform 名称有所不同,但除此之外,我们使用它的方式与前文相同:
materials: [
CustomMaterial {
shadingMode: CustomMaterial.Shaded
fragmentShader: "material_customlights.frag"
property color uDiffuse: "orange"
property real uShininess: 150
}
]片段着色器为所有不同类型的光源实现了自定义逻辑:
void MAIN()
{
SPECULAR_AMOUNT = 1.0;
ROUGHNESS = 0.5;
BASE_COLOR = uDiffuse;
}
void AMBIENT_LIGHT()
{
DIFFUSE += uDiffuse.rgb * TOTAL_AMBIENT_COLOR;
}
void DIRECTIONAL_LIGHT()
{
DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(NORMAL), TO_LIGHT_DIR)));
}
void POINT_LIGHT()
{
DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * LIGHT_ATTENUATION * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(NORMAL), TO_LIGHT_DIR)));
}
void SPOT_LIGHT()
{
DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * LIGHT_ATTENUATION * SPOT_FACTOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
}在此我们使用了许多新的特殊关键字,这些关键字指代各种光照类型的属性。有关每个关键字的描述,请参阅CustomMaterial 文档。请注意,每种光照类型都有其专属的函数。任何未实现的函数将使用默认实现,其行为类似于PrincipledMaterial 。例如:在此着色器中,我们未实现SPECULAR_LIGHT() ,因此将获得内置的镜面反射效果。
添加顶点着色器
自定义材质还可以使用顶点着色器来修改模型的几何体。在此,我们同时指定了片段着色器和顶点着色器,并添加了若干将作为统一值被采用的属性:
materials: [
CustomMaterial {
id: material
shadingMode: CustomMaterial.Shaded
vertexShader: "material_distortion.vert"
fragmentShader: "material_customlights.frag"
property real uTime: 0.0
property real uAmplitude: 0.3
property color uDiffuse: "yellow"
property real uShininess: 50
NumberAnimation {
target: material
property: "uTime"
from: 0.0
to: 31.4
duration: 10000
loops: Animation.Infinite
running: true
}
}
]顶点着色器非常简短:
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
void MAIN()
{
VERTEX.y += sin(uTime + VERTEX.x*10.0) * uAmplitude;
}这会根据随时间变化的正弦波对每个顶点进行位移,从而变形模型。
透明材质
最后,我们添加一个带有透明材质的球体。出于性能考虑,Qt 并未以完全逼真的方式实现透明效果。相反,Qt 会将场景中所有不透明物体渲染到一张纹理上,然后让透明材质从该纹理中读取数据。这意味着,当透明材质置于其他物体前方时,效果最佳:
Model {
id: screenSphere
source: "#Sphere"
scale: Qt.vector3d(0.75, 0.75, 0.75)
y: 60
z: 750;
materials: [
CustomMaterial {
shadingMode: CustomMaterial.Shaded
fragmentShader: "material_transparent.frag"
}
]在此示例中,我们实现了一个简化的失真函数,该函数并未尝试模拟真实的物理折射:
void MAIN()
{
vec2 size = vec2(textureSize(SCREEN_TEXTURE, 0));
vec2 uv = FRAGCOORD.xy / size;
vec3 view = normalize(VIEW_VECTOR);
vec3 projection = view - view * normalize(NORMAL);
vec3 refraction = projection * projection;
uv += refraction.xy * 0.5;
vec3 col = texture(SCREEN_TEXTURE, uv).rgb;
col = col * 0.8 + vec3(0.2);
BASE_COLOR = vec4(col, 1.0);
}SCREEN_TEXTURE 指的是显示场景中所有不透明物体的纹理。我们首先计算该纹理中与当前顶点屏幕位置对应的 UV 坐标,然后对该位置添加偏移量以模拟折射效果,最后进行纹理查找。
最后,我们混合 20% 的白色以获得轻微的朦胧感。请注意,输出被赋值给 `BASE_COLOR`,因此 Qt 会在此基础上添加光照。这就是为什么我们能在球体表面看到反射的原因。
无着色材质
还可以创建使用完整着色器程序的自定义材质(同时仍可使用便捷关键字)。customshaders示例演示了另一类自定义材质:无着色自定义材质。
© 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.