QML中的3D功能--自定义着色器开发
在 Qt 3D 中使用自定义着色器可以实现高度定制化的渲染效果。以下是完整的自定义着色器开发方案。
一、基础着色器创建
1. 创建自定义材质
qml
import Qt3D.Core 2.15
import Qt3D.Render 2.15
import Qt3D.Extras 2.15Entity {components: [Transform { translation: Qt.vector3d(0, 0, -5) },CuboidMesh {},Material {effect: Effect {techniques: [// OpenGL技术Technique {graphicsApiFilter {api: GraphicsApiFilter.OpenGLprofile: GraphicsApiFilter.CoreProfilemajorVersion: 3minorVersion: 3}renderPasses: [RenderPass {shaderProgram: ShaderProgram {vertexShaderCode: "#version 330in vec3 vertexPosition;in vec3 vertexNormal;uniform mat4 mvp;out vec3 vNormal;void main() {gl_Position = mvp * vec4(vertexPosition, 1.0);vNormal = vertexNormal;}"fragmentShaderCode: "#version 330in vec3 vNormal;out vec4 fragColor;void main() {vec3 lightDir = normalize(vec3(1,1,1));float diff = max(dot(vNormal, lightDir), 0.0);fragColor = vec4(diff * vec3(1.0,0.5,0.2), 1.0);}"}}]},// Vulkan技术Technique {graphicsApiFilter {api: GraphicsApiFilter.VulkanmajorVersion: 1minorVersion: 0}// Vulkan着色器代码...}]}}]
}
二、高级着色器技术
1. 着色器参数传递
qml
Material {parameters: [// 统一变量Parameter { name: "time"; value: timeUniform.value },Parameter { name: "color"; value: "blue" },// 纹理Parameter {name: "diffuseTexture"value: Texture2D {generateMipMaps: trueminificationFilter: Texture.LinearMipMapLinearmagnificationFilter: Texture.LinearwrapMode {x: WrapMode.Repeaty: WrapMode.Repeat}TextureImage {source: "texture.png"}}}]
}// 动画化参数
NumberAnimation {id: timeUniformtarget: timeUniformproperty: "value"from: 0to: 100duration: 5000loops: Animation.Infinite
}
2. 几何着色器示例
qml
ShaderProgram {geometryShaderCode: "#version 330layout(triangles) in;layout(triangle_strip, max_vertices=3) out;in vec3 vNormal[];out vec3 gNormal;uniform float explodeAmount;void main() {vec3 avgPos = (gl_in[0].gl_Position.xyz + gl_in[1].gl_Position.xyz + gl_in[2].gl_Position.xyz) / 3.0;for(int i=0; i<3; i++) {vec3 dir = normalize(gl_in[i].gl_Position.xyz - avgPos);gl_Position = gl_in[i].gl_Position + vec4(dir * explodeAmount, 0.0);gNormal = vNormal[i];EmitVertex();}EndPrimitive();}"
}
3. 计算着色器 (Qt 6.2+)
qml
ComputeCommand {workGroupX: 32workGroupY: 32workGroupZ: 1shaderProgram: ShaderProgram {computeShaderCode: "#version 430layout(local_size_x = 32, local_size_y = 32) in;layout(rgba32f, binding = 0) uniform image2D outputImage;void main() {ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy);vec4 color = vec4(float(pixelCoords.x)/1024.0,float(pixelCoords.y)/768.0,0.5,1.0);imageStore(outputImage, pixelCoords, color);}"}
}
三、着色器特效实现
1. 卡通渲染 (Toon Shading)
glsl
// 顶点着色器
vertexShaderCode: "#version 330in vec3 vertexPosition;in vec3 vertexNormal;uniform mat4 mvp;uniform mat4 modelMatrix;out vec3 vNormal;out vec3 vPosition;void main() {gl_Position = mvp * vec4(vertexPosition, 1.0);vNormal = normalize(mat3(modelMatrix) * vertexNormal);vPosition = vec3(modelMatrix * vec4(vertexPosition, 1.0));}
"// 片段着色器
fragmentShaderCode: "#version 330in vec3 vNormal;in vec3 vPosition;out vec4 fragColor;uniform vec3 lightPos = vec3(5,5,5);uniform vec3 lightColor = vec3(1,1,1);uniform vec3 objectColor = vec3(0.5,0.3,0.8);void main() {vec3 lightDir = normalize(lightPos - vPosition);float diff = max(dot(vNormal, lightDir), 0.0);// 离散化光照if(diff > 0.8) diff = 1.0;else if(diff > 0.5) diff = 0.6;else if(diff > 0.2) diff = 0.3;else diff = 0.1;vec3 diffuse = diff * lightColor;vec3 result = (diffuse) * objectColor;// 添加轮廓float edge = max(dot(vNormal, vec3(0,0,1)), 0.0);if(edge < 0.2) result = vec3(0,0,0);fragColor = vec4(result, 1.0);}
"
2. 水波纹效果
glsl
// 顶点着色器
vertexShaderCode: "#version 330in vec3 vertexPosition;in vec2 vertexTexCoord;uniform mat4 mvp;uniform float time;out vec2 vTexCoord;void main() {float wave = sin(time + vertexPosition.x * 5.0) * 0.1;vec3 pos = vertexPosition + vec3(0, wave, 0);gl_Position = mvp * vec4(pos, 1.0);vTexCoord = vertexTexCoord;}
"// 片段着色器
fragmentShaderCode: "#version 330in vec2 vTexCoord;uniform sampler2D waterTexture;uniform float time;out vec4 fragColor;void main() {vec2 distortedTexCoord = vTexCoord + vec2(sin(time + vTexCoord.y * 10.0) * 0.02,cos(time + vTexCoord.x * 10.0) * 0.02);vec4 color = texture(waterTexture, distortedTexCoord);// 添加高光float spec = pow(max(0.0, sin(vTexCoord.x * 50.0 + time * 5.0)), 10.0);color.rgb += vec3(spec * 0.5);fragColor = color;}
"
四、性能优化技巧
1)着色器变体管理:
qml
Technique {filterKeys: [FilterKey { name: "renderingStyle"; value: "forward" },FilterKey { name: "lightingModel"; value: "phong" }]// ...
}
2)着色器预编译:
qml
ShaderProgram {id: precompiledShadervertexShaderCode: loadShaderSource("shaders/precompiled.vert.qsb")fragmentShaderCode: loadShaderSource("shaders/precompiled.frag.qsb")function loadShaderSource(file) {var request = new XMLHttpRequest()request.open("GET", file, false)request.send(null)return request.responseText}
}
3)着色器LOD:
qml
LevelOfDetail {thresholds: [10, 50, 100]currentIndex: calculateLodIndex()Technique { /* 高细节着色器 */ }Technique { /* 中细节着色器 */ }Technique { /* 低细节着色器 */ }
}
五、调试与问题排查
1)着色器错误捕获:
qml
ShaderProgram {onLogChanged: console.log("Shader Log:", log)onStatusChanged: {if (status === ShaderProgram.Error)console.error("Shader Error:", log)}
}
2)帧调试器集成:
qml
RenderSettings {activeFrameGraph: ForwardRenderer {// ...渲染设置...DebugOverlay {shaderInfoEnabled: true}}
}
3)变量可视化调试:
glsl
// 临时调试输出
fragColor = vec4(vNormal * 0.5 + 0.5, 1.0); // 可视化法线
// fragColor = vec4(vec3(gl_FragCoord.z), 1.0); // 可视化深度
六、完整示例:波浪变形+边缘高光效果
1. QML主文件 (main.qml)
import QtQuick 2.15
import Qt3D.Core 2.15
import Qt3D.Render 2.15
import Qt3D.Extras 2.15
import QtQuick.Controls 2.15ApplicationWindow {width: 1280height: 720visible: trueView3D {anchors.fill: parentcamera: cameraEntity {id: sceneRootCamera {id: cameraprojectionType: CameraLens.PerspectiveProjectionfieldOfView: 45aspectRatio: 16/9nearPlane: 0.1farPlane: 1000.0position: Qt.vector3d(0, 1, 6)upVector: Qt.vector3d(0, 1, 0)viewCenter: Qt.vector3d(0, 0, 0)}components: [RenderSettings {activeFrameGraph: ForwardRenderer {clearColor: "black"camera: camera}},InputSettings {}]// 自定义着色器实体Entity {id: customShaderEntitycomponents: [Transform {id: entityTransformtranslation: Qt.vector3d(0, -0.5, 0)rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), -45)},TorusMesh {id: torusMeshradius: 1.5minorRadius: 0.5rings: 50slices: 50},CustomMaterial {id: customMaterial// 参数通过QML控制time: timeSlider.valuewaveHeight: waveHeightSlider.valuehighlightColor: colorPicker.color}]}// 环境光Entity {components: [DirectionalLight {worldDirection: Qt.vector3d(0.3, -1, 0.2).normalized()color: Qt.rgba(0.5, 0.5, 0.5, 1)intensity: 0.5}]}}}// 控制面板Pane {anchors.bottom: parent.bottomanchors.horizontalCenter: parent.horizontalCenterwidth: parent.width * 0.8height: 150Column {spacing: 5Row {spacing: 10Label { text: "时间: "; anchors.verticalCenter: parent.verticalCenter }Slider {id: timeSliderfrom: 0to: 100value: 0width: 200}Label { text: "波浪高度: "; anchors.verticalCenter: parent.verticalCenter }Slider {id: waveHeightSliderfrom: 0to: 1value: 0.3width: 200}}Row {spacing: 10Label { text: "高光颜色: " }ColorDialog {id: colorDialogtitle: "选择高光颜色"}Button {text: "选择颜色"onClicked: colorDialog.open()}Rectangle {width: 30height: 30color: colorPicker.colorborder.width: 1}}}}// 颜色选择器ColorDialog {id: colorPickercolor: "cyan"}// 自动动画NumberAnimation {target: timeSliderproperty: "value"from: 0to: 100duration: 10000loops: Animation.Infiniterunning: true}
}
2. 自定义材质 (CustomMaterial.qml)
import Qt3D.Core 2.15
import Qt3D.Render 2.15Material {id: root// 可绑定属性property real time: 0.0property real waveHeight: 0.3property color highlightColor: "cyan"// 材质参数parameters: [Parameter { name: "time"; value: root.time },Parameter { name: "waveHeight"; value: root.waveHeight },Parameter { name: "highlightColor"; value: root.highlightColor }]// 材质效果effect: Effect {techniques: [// OpenGL技术Technique {graphicsApiFilter {api: GraphicsApiFilter.OpenGLprofile: GraphicsApiFilter.CoreProfilemajorVersion: 3minorVersion: 3}renderPasses: [RenderPass {shaderProgram: ShaderProgram {id: gl3ShaderProgram// 顶点着色器vertexShaderCode: "#version 330 corein vec3 vertexPosition;in vec3 vertexNormal;in vec2 vertexTexCoord;uniform mat4 modelViewProjection;uniform mat4 modelMatrix;uniform float time;uniform float waveHeight;out vec3 vNormal;out vec3 vPosition;out vec2 vTexCoord;void main() {// 波浪变形float wave = sin(time + vertexPosition.x * 5.0) * cos(time + vertexPosition.z * 3.0) * waveHeight;vec3 displacedPosition = vertexPosition + vec3(0.0, wave, 0.0);gl_Position = modelViewProjection * vec4(displacedPosition, 1.0);// 计算变形后的法线float dx = cos(time + vertexPosition.x * 5.0) * 5.0 * waveHeight;float dz = -sin(time + vertexPosition.z * 3.0) * 3.0 * waveHeight;vec3 tangent = normalize(vec3(1.0, dx, 0.0));vec3 bitangent = normalize(vec3(0.0, dz, 1.0));vNormal = normalize(cross(tangent, bitangent));vPosition = vec3(modelMatrix * vec4(displacedPosition, 1.0));vTexCoord = vertexTexCoord;}"// 片段着色器fragmentShaderCode: "#version 330 corein vec3 vNormal;in vec3 vPosition;in vec2 vTexCoord;uniform vec3 highlightColor;uniform float time;out vec4 fragColor;void main() {// 基础颜色vec3 baseColor = vec3(0.2, 0.4, 0.8);// 光照计算vec3 lightPos = vec3(3.0, 5.0, 2.0);vec3 lightDir = normalize(lightPos - vPosition);vec3 viewDir = normalize(-vPosition);vec3 reflectDir = reflect(-lightDir, vNormal);// 漫反射float diff = max(dot(vNormal, lightDir), 0.0);vec3 diffuse = diff * vec3(1.0);// 镜面反射 (Blinn-Phong)vec3 halfwayDir = normalize(lightDir + viewDir);float spec = pow(max(dot(vNormal, halfwayDir), 0.0), 32.0);vec3 specular = spec * highlightColor;// 边缘高光float rim = 1.0 - max(dot(viewDir, vNormal), 0.0);rim = smoothstep(0.7, 1.0, rim);vec3 rimLight = rim * highlightColor * 0.8;// 组合所有光照vec3 result = (diffuse + specular + rimLight) * baseColor;// 添加基于UV的图案float pattern = sin(vTexCoord.x * 20.0 + time) * sin(vTexCoord.y * 20.0 + time);pattern = smoothstep(-0.3, 0.3, pattern);result = mix(result, vec3(1.0), pattern * 0.3);fragColor = vec4(result, 1.0);}"}// 渲染状态配置renderStates: [CullFace { mode: CullFace.Back },DepthTest { depthFunction: DepthTest.Less }]}]},// Vulkan技术 (可选)Technique {graphicsApiFilter {api: GraphicsApiFilter.VulkanmajorVersion: 1minorVersion: 0}// Vulkan着色器代码...}]}
}
3. 效果说明
这个完整示例实现了以下效果:
-
波浪变形:顶点着色器中基于时间的正弦波变形
-
动态法线计算:根据变形表面重新计算法线
-
多光源组合:包含漫反射、镜面反射和边缘高光
-
UV图案效果:基于纹理坐标的动画图案
-
QML控制:通过滑块控制波浪高度和时间动画
-
颜色选择:可交互选择高光颜色
4. 扩展建议
1)添加纹理支持:
qml
// 在CustomMaterial.qml中添加
property alias texture: textureParam.valueParameter {id: textureParamname: "diffuseTexture"value: Texture2D {generateMipMaps: trueminificationFilter: Texture.LinearMipMapLinearmagnificationFilter: Texture.LinearTextureImage {source: "texture.png"}}
}
2)添加几何着色器:
glsl
// 在ShaderProgram中添加
geometryShaderCode: "#version 330 corelayout(triangles) in;layout(triangle_strip, max_vertices=3) out;in vec3 vNormal[];out vec3 gNormal;uniform float explodeAmount;void main() {vec3 avgPos = (gl_in[0].gl_Position.xyz + gl_in[1].gl_Position.xyz + gl_in[2].gl_Position.xyz) / 3.0;for(int i=0; i<3; i++) {vec3 dir = normalize(gl_in[i].gl_Position.xyz - avgPos);gl_Position = gl_in[i].gl_Position + vec4(dir * explodeAmount, 0.0);gNormal = vNormal[i];EmitVertex();}EndPrimitive();}
"
3)性能优化:
qml
// 使用预编译的着色器二进制文件
ShaderProgram {vertexShaderCode: loadShader("shaders/wave.vert.qsb")fragmentShaderCode: loadShader("shaders/wave.frag.qsb")function loadShader(file) {var xhr = new XMLHttpRequestxhr.open("GET", file, false)xhr.send()return xhr.responseText}
}