aboutsummaryrefslogtreecommitdiffstats
path: root/src/runtimerender/res/effectlib/transmission.glsllib
blob: 93d6a9d75b0f349c7661a1eb93a1d48efc41e093 (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
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include "bsdf.glsllib"

#ifdef QQ3D_SHADER_META
/*{
    "uniforms": [
        { "type": "sampler2D", "name": "qt_screenTexture", "multiview_dependent": true },
        { "type": "mat4", "name": "qt_modelMatrix" },
        { "type": "mat4", "name": "qt_viewProjectionMatrix", "multiview_dependent": true }
    ]
}*/
#endif // QQ3D_SHADER_META

// Compute attenuated light as it travels through a volume.
vec3 qt_applyVolumeAttenuation(in vec3 radiance, in float transmissionDistance, in vec3 attenuationColor, in float attenuationDistance)
{
    if (attenuationDistance == 0.0) {
        return radiance;
    } else {
        // Compute light attenuation using Beer's law.
        vec3 attenuationCoefficient = -log(attenuationColor) / attenuationDistance;
        vec3 transmittance = exp(-attenuationCoefficient * transmissionDistance); // Beer's law
        return transmittance * radiance;
    }
}

vec3 qt_getVolumeTransmissionRay(in vec3 n, in vec3 v, in float thickness, in float ior)
{
    // Direction of refracted light.
    vec3 refractionVector = refract(-v, normalize(n), 1.0 / ior);

    // Compute rotation-independent scaling of the model matrix.
    vec3 modelScale;
    modelScale.x = length(vec3(qt_modelMatrix[0].xyz));
    modelScale.y = length(vec3(qt_modelMatrix[1].xyz));
    modelScale.z = length(vec3(qt_modelMatrix[2].xyz));

    // The thickness is specified in local space.
    return normalize(refractionVector) * thickness * modelScale;
}

// Needed to correct the framebuffer texture for certain RHI backends
vec2 qt_correctFrambufferUV(in vec2 uv)
{
    if (qt_rhi_properties.x < 0 && qt_rhi_properties.y == 1)
        uv.y = 1 - uv.y;
    return uv;
}

vec3 qt_getTransmissionSample(in vec2 fragCoord, in float roughness, in float ior)
{
    const vec2 fbCoord = qt_correctFrambufferUV(fragCoord);
#if QSHADER_VIEW_COUNT >= 2
    const float framebufferLod = log2(float(textureSize(qt_screenTextureArray, 0).x)) * qt_applyIorToRoughness(roughness, ior);
    vec3 transmittedLight = textureLod(qt_screenTextureArray, vec3(fbCoord, qt_viewIndex), framebufferLod).rgb;
#else
    const float framebufferLod = log2(float(textureSize(qt_screenTexture, 0).x)) * qt_applyIorToRoughness(roughness, ior);
    vec3 transmittedLight = textureLod(qt_screenTexture, fbCoord, framebufferLod).rgb;
#endif
    return transmittedLight;
}

vec3 qt_getIBLVolumeRefraction(in vec3 n, in vec3 v, in float perceptualRoughness, in vec3 baseColor, in vec3 F,
    in vec3 position, in float ior, in float thickness, in vec3 attenuationColor, in float attenuationDistance)
{
    vec3 transmissionRay = qt_getVolumeTransmissionRay(n, v, thickness, ior);
    vec3 refractedRayExit = position + transmissionRay;

    // Project refracted vector on the framebuffer, while mapping to normalized device coordinates.
#if QSHADER_VIEW_COUNT >= 2
    vec4 ndcPos = qt_viewProjectionMatrix[qt_viewIndex] * vec4(refractedRayExit, 1.0);
#else
    vec4 ndcPos = qt_viewProjectionMatrix * vec4(refractedRayExit, 1.0);
#endif
    vec2 refractionCoords = ndcPos.xy / ndcPos.w;
    refractionCoords += 1.0;
    refractionCoords /= 2.0;

    // Sample framebuffer to get pixel the refracted ray hits.
    vec3 transmittedLight = qt_getTransmissionSample(refractionCoords, perceptualRoughness, ior);

    vec3 attenuatedColor = qt_applyVolumeAttenuation(transmittedLight, length(transmissionRay), attenuationColor, attenuationDistance);

    // Sample GGX LUT to get the specular component.
    float NdotV = clamp(dot(n, v), 0.0, 1.0);
    vec2 brdf = qt_brdfApproximation(n, v, perceptualRoughness);
    vec3 specularColor = (F * brdf.x + brdf.y);

    return (1.0 - specularColor) * attenuatedColor * baseColor;
}