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

发布于:2025-04-20 ⋅ 阅读:(14) ⋅ 点赞:(0)

在 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
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到