summaryrefslogtreecommitdiffstats
path: root/src/datavisualization/engine/shaders/texture3d.frag
blob: 1192ae8544b0ec24ced630eb118b7ce45d079382 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#version 120

varying highp vec3 pos;

uniform highp sampler3D textureSampler;
uniform highp vec3 cameraPositionRelativeToModel;
uniform highp vec4 colorIndex[256];
uniform highp int color8Bit;
uniform highp vec3 textureDimensions;
uniform highp int sampleCount; // This is the maximum sample count
uniform highp float alphaMultiplier;
uniform highp int preserveOpacity;

const highp float alphaThreshold = 0.0001;

void main() {
    highp vec3 rayDir = -(cameraPositionRelativeToModel - pos);
    vec3 rayStart = pos;
    // Flip Y and Z so QImage bits work directly for texture and first image is in the front
    rayDir.yz = -rayDir.yz;
    rayStart.yz = -rayStart.yz;

    // Calculate ray intersection endpoint
    vec3 rayStop;
    if (rayDir.x == 0.0) {
        rayStop.yz = rayStart.yz;
        rayStop.x = -rayStart.x;
    } else if (rayDir.y == 0.0) {
        rayStop.xz = rayStart.xz;
        rayStop.y = -rayStart.y;
    } else if (rayDir.z == 0.0) {
        rayStop.xy = rayStart.xy;
        rayStop.z = -rayStart.z;
    } else {
        highp vec3 boxBounds = vec3(1.0, 1.0, 1.0);
        highp vec3 invRayDir = 1.0 / rayDir;
        if (rayDir.x < 0)
            boxBounds.x = -1.0;
        if (rayDir.y < 0)
            boxBounds.y = -1.0;
        if (rayDir.z < 0)
            boxBounds.z = -1.0;
        highp vec3 t = (boxBounds - rayStart) * invRayDir;
        highp float minT = min(t.x, t.y);
        minT = min(minT, t.z);
        rayStop = rayStart + minT * rayDir;
    }

    highp vec4 destColor = vec4(0, 0, 0, 0);
    highp float totalAlpha = 0.0;

    if (rayStart != rayStop) {
        // Convert intersections to texture coords
        rayStart = 0.5 * (rayStart + 1.0);
        rayStop = 0.5 * (rayStop + 1.0);

        highp vec3 ray = rayStop - rayStart;
        highp float fullDist = abs(length(ray));
        highp float rayX = abs(ray.x) * textureDimensions.x;
        highp float rayY = abs(ray.y) * textureDimensions.y;
        highp float rayZ = abs(ray.z) * textureDimensions.z;
        highp float maxRayDim = max(rayX, rayY);
        maxRayDim = max(maxRayDim, rayZ);
        int maxCount = int(floor(maxRayDim));

        highp vec3 step = ray / maxRayDim;
        highp float stepSize = fullDist / maxRayDim;

        rayStart += (step * 0.001);
        highp vec3 curPos = rayStart;

        // Adjust alpha multiplier according to the step size to get uniform alpha effect
        // regardless of the ray angle.
        highp float totalAlphaMultiplier = (stepSize / (1.0 / sampleCount)) * alphaMultiplier;

        highp vec4 curColor = vec4(0, 0, 0, 0);
        highp vec3 curRgb = vec3(0, 0, 0);
        highp float curAlpha = 0.0;

        // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1
        for (int i = 0; i < sampleCount; i++) {
            curColor = texture3D(textureSampler, curPos);
            if (color8Bit != 0)
                curColor = colorIndex[int(curColor.r * 255.0)];

            // Unless we have explicit alpha multiplier, we want to preserve opacity anyway
            if (curColor.a == 1.0 && (preserveOpacity != 0 || alphaMultiplier == 1.0))
                curAlpha = 1.0;
            else
                curAlpha = clamp(curColor.a * totalAlphaMultiplier, 0.0, 1.0);

            if (curAlpha > alphaThreshold) {
                curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
                destColor.rgb += curRgb;
                totalAlpha += curAlpha;
            }
            if (i == maxCount || totalAlpha >= 1.0)
                break;
            curPos += step;
        }
    }

    // Brighten up the final color if there is some transparency left
    if (totalAlpha > alphaThreshold && totalAlpha < 1.0)
        destColor *= 1.0 / totalAlpha;

    destColor.a = totalAlpha;
    gl_FragColor = clamp(destColor, 0.0, 1.0);
}