Revision control
++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #ifndef TRANSMISSION_GLSLLIB
++ #define TRANSMISSION_GLSLLIB 1
++
++ #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;
++ }
++
++ vec2 qt_getRefractionCoords(in vec3 refractedRayExit)
++ {
++ #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;
++ return refractionCoords;
++ }
++
++ 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
++ #if QSSG_ENABLE_DISPERSION
++ , in float dispersion
++ #endif
++ )
++ {
++ vec3 transmissionRay = qt_getVolumeTransmissionRay(n, v, thickness, ior);
++
++ #if QSSG_ENABLE_DISPERSION
++ // One refraction per color channel, as KHR_materials_dispersion suggests
++ float halfSpread = (ior - 1.0) * 0.025 * dispersion;
++ vec3 iors = vec3(ior - halfSpread, ior, ior + halfSpread);
++
++ vec3 transmittedLight;
++ for (int i = 0; i < 3; ++i) {
++ vec3 dispersedRay = qt_getVolumeTransmissionRay(n, v, thickness, iors[i]);
++ vec2 dispersedCoords = qt_getRefractionCoords(position + dispersedRay);
++ transmittedLight[i] = qt_getTransmissionSample(dispersedCoords, perceptualRoughness, iors[i])[i];
++ }
++ #else
++ vec2 refractionCoords = qt_getRefractionCoords(position + transmissionRay);
++
++ // Sample framebuffer to get pixel the refracted ray hits.
++ vec3 transmittedLight = qt_getTransmissionSample(refractionCoords, perceptualRoughness, ior);
++ #endif
++
++ // Attenuation uses the undispersed ray length for all channels
++ 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;
++ }
++
++ #endif // TRANSMISSION_GLSLLIB