QML中的3D功能--自定义着色器开发

在 Qt 3D 中使用自定义着色器可以实现高度定制化的渲染效果。以下是完整的自定义着色器开发方案。

一、基础着色器创建

1. 创建自定义材质

qml

复制代码
import Qt3D.Core 2.15
import Qt3D.Render 2.15
import Qt3D.Extras 2.15

Entity {
    components: [
        Transform { translation: Qt.vector3d(0, 0, -5) },
        CuboidMesh {},
        
        Material {
            effect: Effect {
                techniques: [
                    // OpenGL技术
                    Technique {
                        graphicsApiFilter {
                            api: GraphicsApiFilter.OpenGL
                            profile: GraphicsApiFilter.CoreProfile
                            majorVersion: 3
                            minorVersion: 3
                        }
                        
                        renderPasses: [
                            RenderPass {
                                shaderProgram: ShaderProgram {
                                    vertexShaderCode: "
                                        #version 330
                                        in vec3 vertexPosition;
                                        in vec3 vertexNormal;
                                        uniform mat4 mvp;
                                        out vec3 vNormal;
                                        
                                        void main() {
                                            gl_Position = mvp * vec4(vertexPosition, 1.0);
                                            vNormal = vertexNormal;
                                        }
                                    "
                                    
                                    fragmentShaderCode: "
                                        #version 330
                                        in 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.Vulkan
                            majorVersion: 1
                            minorVersion: 0
                        }
                        // Vulkan着色器代码...
                    }
                ]
            }
        }
    ]
}

二、高级着色器技术

1. 着色器参数传递

qml

复制代码
Material {
    parameters: [
        // 统一变量
        Parameter { name: "time"; value: timeUniform.value },
        Parameter { name: "color"; value: "blue" },
        
        // 纹理
        Parameter {
            name: "diffuseTexture"
            value: Texture2D {
                generateMipMaps: true
                minificationFilter: Texture.LinearMipMapLinear
                magnificationFilter: Texture.Linear
                wrapMode {
                    x: WrapMode.Repeat
                    y: WrapMode.Repeat
                }
                TextureImage {
                    source: "texture.png"
                }
            }
        }
    ]
}

// 动画化参数
NumberAnimation {
    id: timeUniform
    target: timeUniform
    property: "value"
    from: 0
    to: 100
    duration: 5000
    loops: Animation.Infinite
}

2. 几何着色器示例

qml

复制代码
ShaderProgram {
    geometryShaderCode: "
        #version 330
        layout(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: 32
    workGroupY: 32
    workGroupZ: 1
    
    shaderProgram: ShaderProgram {
        computeShaderCode: "
            #version 430
            layout(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 330
    in 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 330
    in 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 330
    in 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 330
    in 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: precompiledShader
    vertexShaderCode: 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.15

ApplicationWindow {
    width: 1280
    height: 720
    visible: true
    
    View3D {
        anchors.fill: parent
        
        camera: camera
        
        Entity {
            id: sceneRoot
            
            Camera {
                id: camera
                projectionType: CameraLens.PerspectiveProjection
                fieldOfView: 45
                aspectRatio: 16/9
                nearPlane: 0.1
                farPlane: 1000.0
                position: 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: customShaderEntity
                
                components: [
                    Transform {
                        id: entityTransform
                        translation: Qt.vector3d(0, -0.5, 0)
                        rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), -45)
                    },
                    TorusMesh {
                        id: torusMesh
                        radius: 1.5
                        minorRadius: 0.5
                        rings: 50
                        slices: 50
                    },
                    CustomMaterial {
                        id: customMaterial
                        // 参数通过QML控制
                        time: timeSlider.value
                        waveHeight: waveHeightSlider.value
                        highlightColor: 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.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        width: parent.width * 0.8
        height: 150
        
        Column {
            spacing: 5
            
            Row {
                spacing: 10
                
                Label { text: "时间: "; anchors.verticalCenter: parent.verticalCenter }
                Slider {
                    id: timeSlider
                    from: 0
                    to: 100
                    value: 0
                    width: 200
                }
                
                Label { text: "波浪高度: "; anchors.verticalCenter: parent.verticalCenter }
                Slider {
                    id: waveHeightSlider
                    from: 0
                    to: 1
                    value: 0.3
                    width: 200
                }
            }
            
            Row {
                spacing: 10
                
                Label { text: "高光颜色: " }
                ColorDialog {
                    id: colorDialog
                    title: "选择高光颜色"
                }
                
                Button {
                    text: "选择颜色"
                    onClicked: colorDialog.open()
                }
                
                Rectangle {
                    width: 30
                    height: 30
                    color: colorPicker.color
                    border.width: 1
                }
            }
        }
    }
    
    // 颜色选择器
    ColorDialog {
        id: colorPicker
        color: "cyan"
    }
    
    // 自动动画
    NumberAnimation {
        target: timeSlider
        property: "value"
        from: 0
        to: 100
        duration: 10000
        loops: Animation.Infinite
        running: true
    }
}

2. 自定义材质 (CustomMaterial.qml)

复制代码
import Qt3D.Core 2.15
import Qt3D.Render 2.15

Material {
    id: root
    
    // 可绑定属性
    property real time: 0.0
    property real waveHeight: 0.3
    property 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.OpenGL
                    profile: GraphicsApiFilter.CoreProfile
                    majorVersion: 3
                    minorVersion: 3
                }
                
                renderPasses: [
                    RenderPass {
                        shaderProgram: ShaderProgram {
                            id: gl3ShaderProgram
                            
                            // 顶点着色器
                            vertexShaderCode: "
                                #version 330 core
                                
                                in 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 core
                                
                                in 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.Vulkan
                    majorVersion: 1
                    minorVersion: 0
                }
                // Vulkan着色器代码...
            }
        ]
    }
}

3. 效果说明

这个完整示例实现了以下效果:

  1. 波浪变形:顶点着色器中基于时间的正弦波变形

  2. 动态法线计算:根据变形表面重新计算法线

  3. 多光源组合:包含漫反射、镜面反射和边缘高光

  4. UV图案效果:基于纹理坐标的动画图案

  5. QML控制:通过滑块控制波浪高度和时间动画

  6. 颜色选择:可交互选择高光颜色

4. 扩展建议

1)添加纹理支持

qml

复制代码
// 在CustomMaterial.qml中添加
property alias texture: textureParam.value

Parameter {
    id: textureParam
    name: "diffuseTexture"
    value: Texture2D {
        generateMipMaps: true
        minificationFilter: Texture.LinearMipMapLinear
        magnificationFilter: Texture.Linear
        TextureImage {
            source: "texture.png"
        }
    }
}

​​​​​​​2)添加几何着色器:

glsl

复制代码
// 在ShaderProgram中添加
geometryShaderCode: "
    #version 330 core
    layout(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 XMLHttpRequest
        xhr.open("GET", file, false)
        xhr.send()
        return xhr.responseText
    }
}
复制代码
相关推荐
AgilityBaby4 小时前
UE5打包项目设置Project Settings(打包widows exe安装包)
c++·3d·ue5·游戏引擎·unreal engine
广州智造10 小时前
OptiStruct实例:消声器前盖ERP分析(2)RADSND基础理论
数据库·人工智能·算法·机器学习·数学建模·3d·软件构建
lingxiao1688812 小时前
3D拟合测量水杯半径
3d
学步_技术14 小时前
自动驾驶系列—Monocular 3D Lane Detection for Autonomous Driving
人工智能·机器学习·计算机视觉·3d·自动驾驶
GISer_Jing2 天前
[3D GIS&Mesh]三角网格模型中的孔洞修补算法
算法·3d
cnbestec2 天前
MinVerse 3D触觉鼠标的技术原理与创新解析
3d·计算机外设·xr·minverse·minverse 3d触觉鼠标·三维交互·触觉反馈
moonsims3 天前
无人机桥梁3D建模、巡检、检测的航线规划
3d·无人机
西哥写代码3 天前
基于cornerstone3D的dicom影像浏览器 第二十七章 设置vr相机,复位视图
3d·vue3·vr·cornerstonejs
西哥写代码3 天前
基于cornerstone3D的dicom影像浏览器 第二十五章 自定义VR调窗工具
javascript·3d·vue3·vr·cornerstonejs
SOLIDWORKS硕迪制信4 天前
3D PDF如何制作?SOLIDWORKS MBD模板定制技巧
3d·3d pdf·mbd·solidworks mbd