Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Can't seem to get shaders to work.

Can't seem to get shaders to work.

Scheduled Pinned Locked Moved Unsolved General and Desktop
2 Posts 2 Posters 122 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • O Offline
    O Offline
    OlivierD
    wrote on last edited by
    #1

    Hello Everyone

    For the last two weeks I have been trying my hand at using shaders to create a sort of heatmap overlaying the map QML.
    However, no matter what it keeps refusing to show me any coloring.
    I am stumped, so does anyone have any clue as to why this is happening? I have added my .vert and .frag and the main.qml file for reference.

    main.qml:

    import QtQuick
    import QtLocation
    import QtPositioning
    import QtQuick.Controls
    
    ApplicationWindow {
        visible: true
        width: 1024
        height: 768
        title: "Heat Map Overlay - Working Version"
    
        ListModel {
            id: dataModel
            ListElement { latitude: 40.7128; longitude: -74.0060; value: 0.9 }  // New York
            ListElement { latitude: 51.5074; longitude: -0.1278; value: 0.7 }   // London
            ListElement { latitude: 35.6762; longitude: 139.6503; value: 0.8 }  // Tokyo
            ListElement { latitude: -33.8688; longitude: 151.2093; value: 0.6 } // Sydney
            ListElement { latitude: 48.8566; longitude: 2.3522; value: 0.85 }   // Paris
            ListElement { latitude: 55.7558; longitude: 37.6173; value: 0.4 }   // Moscow
            ListElement { latitude: -23.5505; longitude: -46.6333; value: 0.5 } // São Paulo
            ListElement { latitude: 19.4326; longitude: -99.1332; value: 0.65 } // Mexico City
            ListElement { latitude: 28.6139; longitude: 77.2090; value: 0.3 }   // New Delhi
            ListElement { latitude: 1.3521; longitude: 103.8198; value: 0.75 }  // Singapore
        }
    
        Map {
            id: map
            anchors.fill: parent
            plugin: Plugin {
                name: "osm"
            }
            center: QtPositioning.coordinate(30, 0)
            zoomLevel: 3
    
            onCenterChanged: updateTimer.restart()
            onZoomLevelChanged: updateTimer.restart()
    
            // Debug markers
            MapItemView {
                model: showMarkersCheck.checked ? dataModel : null
                delegate: MapCircle {
                    center: QtPositioning.coordinate(model.latitude, model.longitude)
                    radius: 50000
                    color: Qt.rgba(1, 0, 0, 0.3)
                    border.width: 2
                    border.color: "red"
                }
            }
        }
    
        Timer {
            id: updateTimer
            interval: 100
            onTriggered: heatmapOverlay.updatePoints()
        }
    
        ShaderEffect {
            id: heatmapOverlay
            anchors.fill: map
            blending: true
            opacity: opacitySlider.value
    
            // Individual point properties (Qt 6.8 doesn't handle arrays well)
            property vector4d point0: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point1: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point2: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point3: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point4: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point5: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point6: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point7: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point8: Qt.vector4d(-1, -1, 0, 0)
            property vector4d point9: Qt.vector4d(-1, -1, 0, 0)
    
            property int pointCount: 0
            property real influenceRadius: 0.2  // In normalized coordinates
    
            function updatePoints() {
                var count = Math.min(dataModel.count, 10)
                var points = []
    
                for (var i = 0; i < count; i++) {
                    var item = dataModel.get(i)
                    var coord = QtPositioning.coordinate(item.latitude, item.longitude)
                    var screenPos = map.fromCoordinate(coord)
    
                    // Normalize to 0-1 range
                    var normalizedX = screenPos.x / width
                    var normalizedY = screenPos.y / height
    
                    // Store in temporary array
                    points.push(Qt.vector4d(normalizedX, normalizedY, item.value, 1.0))
                }
    
                // Assign to individual properties
                point0 = points[0] || Qt.vector4d(-1, -1, 0, 0)
                point1 = points[1] || Qt.vector4d(-1, -1, 0, 0)
                point2 = points[2] || Qt.vector4d(-1, -1, 0, 0)
                point3 = points[3] || Qt.vector4d(-1, -1, 0, 0)
                point4 = points[4] || Qt.vector4d(-1, -1, 0, 0)
                point5 = points[5] || Qt.vector4d(-1, -1, 0, 0)
                point6 = points[6] || Qt.vector4d(-1, -1, 0, 0)
                point7 = points[7] || Qt.vector4d(-1, -1, 0, 0)
                point8 = points[8] || Qt.vector4d(-1, -1, 0, 0)
                point9 = points[9] || Qt.vector4d(-1, -1, 0, 0)
    
                pointCount = count
    
                console.log("Updated", count, "points for shader")
            }
    
            Component.onCompleted: updatePoints()
    
            vertexShader: "qrc:/shaders/heatmap.vert.qsb"
            fragmentShader: "qrc:/shaders/heatmap_simple.frag.qsb"
        }
    
        // Controls
        Rectangle {
            anchors.right: parent.right
            anchors.top: parent.top
            anchors.margins: 10
            width: 200
            height: controlCol.height + 20
            color: Qt.rgba(0.1, 0.1, 0.1, 0.9)
            radius: 10
    
            Column {
                id: controlCol
                anchors.centerIn: parent
                spacing: 10
    
                Text {
                    text: "Controls"
                    color: "white"
                    font.bold: true
                }
    
                Row {
                    spacing: 10
                    Text {
                        text: "Opacity:"
                        color: "white"
                        width: 60
                    }
                    Slider {
                        id: opacitySlider
                        width: 120
                        from: 0
                        to: 1
                        value: 0.7
                    }
                }
    
                CheckBox {
                    id: showMarkersCheck
                    text: "Show Markers"
                    palette.windowText: "white"
                }
    
                Text {
                    text: "Zoom: " + map.zoomLevel.toFixed(2)
                    color: "white"
                }
            }
        }
    }
    
    

    heatmap.vert:

    #version 440
    
    layout(location = 0) in vec4 qt_Vertex;
    layout(location = 1) in vec2 qt_MultiTexCoord0;
    
    layout(location = 0) out vec2 texCoord;
    
    layout(std140, binding = 0) uniform buf {
        mat4 qt_Matrix;
        float qt_Opacity;
    };
    
    void main() {
        texCoord = qt_MultiTexCoord0;
        gl_Position = qt_Matrix * qt_Vertex;
    }
    
    

    heatmap.frag

    #version 440
    
    layout(location = 0) in vec2 qt_TexCoord0;
    layout(location = 0) out vec4 fragColor;
    
    layout(std140, binding = 0) uniform buf {
        mat4 qt_Matrix;
        float qt_Opacity;
    
        // Custom uniforms
        vec4 dataPoints[10];      // x, y (in pixels), value, padding
        int actualPointCount;      // Number of valid points
        float mapZoom;            // Current map zoom level
        vec2 mapCenter;           // Map center (lon, lat)
        float influenceRadius;    // Influence radius in pixels
    } ubuf;
    
    // Smooth heat map gradient
    vec3 getHeatMapColor(float value) {
        value = clamp(value, 0.0, 1.0);
    
        // Create smooth gradient with more color stops
        vec3 color;
    
        if (value < 0.2) {
            // Dark blue to blue
            float t = value * 5.0;
            color = mix(vec3(0.0, 0.0, 0.3), vec3(0.0, 0.0, 1.0), t);
        } else if (value < 0.35) {
            // Blue to cyan
            float t = (value - 0.2) * 6.67;
            color = mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 0.7, 1.0), t);
        } else if (value < 0.5) {
            // Cyan to green
            float t = (value - 0.35) * 6.67;
            color = mix(vec3(0.0, 0.7, 1.0), vec3(0.0, 1.0, 0.0), t);
        } else if (value < 0.65) {
            // Green to yellow-green
            float t = (value - 0.5) * 6.67;
            color = mix(vec3(0.0, 1.0, 0.0), vec3(0.5, 1.0, 0.0), t);
        } else if (value < 0.8) {
            // Yellow-green to yellow
            float t = (value - 0.65) * 6.67;
            color = mix(vec3(0.5, 1.0, 0.0), vec3(1.0, 1.0, 0.0), t);
        } else if (value < 0.9) {
            // Yellow to orange
            float t = (value - 0.8) * 10.0;
            color = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.5, 0.0), t);
        } else {
            // Orange to red
            float t = (value - 0.9) * 10.0;
            color = mix(vec3(1.0, 0.5, 0.0), vec3(1.0, 0.0, 0.0), t);
        }
    
        return color;
    }
    
    // Improved IDW with Gaussian falloff
    float getInterpolatedValue(vec2 pixelCoord) {
        if (ubuf.actualPointCount == 0) {
            return 0.0;
        }
    
        float totalWeight = 0.0;
        float totalValue = 0.0;
    
        // Parameters for interpolation
        float power = 2.5;  // IDW power parameter
        float minDist = 0.5;  // Minimum distance to avoid singularity
    
        // Check if we're very close to any data point
        float closestDist = 1e10;
        float closestValue = 0.0;
    
        for (int i = 0; i < ubuf.actualPointCount; i++) {
            vec2 pointPos = ubuf.dataPoints[i].xy;
            float pointValue = ubuf.dataPoints[i].z;
    
            // Skip invalid points
            if (pointPos.x < -100.0) continue;
    
            float dist = distance(pixelCoord, pointPos);
    
            if (dist < closestDist) {
                closestDist = dist;
                closestValue = pointValue;
            }
    
            // If very close to a point, return its value
            if (dist < minDist) {
                return pointValue;
            }
    
            // Calculate influence based on distance and radius
            float normalizedDist = dist / ubuf.influenceRadius;
    
            // Only consider points within influence radius
            if (normalizedDist < 1.0) {
                // Combine IDW with Gaussian falloff for smoother results
                float idwWeight = 1.0 / pow(dist, power);
                float gaussianWeight = exp(-normalizedDist * normalizedDist * 2.0);
                float weight = idwWeight * gaussianWeight;
    
                totalWeight += weight;
                totalValue += weight * pointValue;
            }
        }
    
        // If we have accumulated weight, return weighted average
        if (totalWeight > 0.0) {
            return totalValue / totalWeight;
        }
    
        // If no points in influence radius, use closest point with strong falloff
        if (closestDist < ubuf.influenceRadius * 2.0) {
            float falloff = 1.0 - (closestDist / (ubuf.influenceRadius * 2.0));
            return closestValue * falloff * falloff;
        }
    
        return 0.0;
    }
    
    void main() {
        // Convert normalized texture coordinates to pixel coordinates
        vec2 pixelCoord = qt_TexCoord0 * vec2(textureSize(qt_Matrix, 0));
    
        // Get interpolated value
        float value = getInterpolatedValue(pixelCoord);
    
        // Only render if we have a meaningful value
        if (value > 0.01) {
            vec3 color = getHeatMapColor(value);
    
            // Fade out at edges of influence
            float alpha = smoothstep(0.0, 0.1, value) * 0.9;
    
            fragColor = vec4(color, alpha) * ubuf.qt_Opacity;
        } else {
            fragColor = vec4(0.0, 0.0, 0.0, 0.0);
        }
    }
    
    

    Any help would be very appreciated
    Kind regards
    Olivier

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jutraim
      wrote on last edited by
      #2

      Hey! It seems like it is impossible to pass javascript arrays to ShaderEffect shader parameters. I have been looking for a solution for this in pure QML as well. The best workaround is to pass an image and use the channels as info, this is prone to floating point precision issues though.

      https://bugreports.qt.io/browse/QTBUG-50493

      1 Reply Last reply
      0

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved