Repository: google/filament
File: shaders/src/surface_light_indirect.fs (F#)

Code before:
#if REFRACTION_TYPE == REFRACTION_TYPE_SOLID
        refractionSolidSphere(etaIR, etaRI, pixel.thickness, n0, -shading_view, ray);
#elif REFRACTION_TYPE == REFRACTION_TYPE_THIN
        refractionThinSphere(etaIR, pixel.uThickness, n0, -shading_view, ray);
#else
#error invalid REFRACTION_TYPE
#endif

        // compute transmission T
#if defined(MATERIAL_HAS_ABSORPTION)
        vec3 T = min(vec3(1.0), exp(-pixel.absorption * ray.d));
#endif

        // Roughness remapping so that an IOR of 1.0 means no microfacet refraction and an IOR
        // of 1.5 has full microfacet refraction
        float perceptualRoughness = mix(pixel.perceptualRoughnessUnclamped, 0.0,
                saturate(etaIR * 3.0 - 2.0));
#if REFRACTION_TYPE == REFRACTION_TYPE_THIN
        // For thin surfaces, the light will bounce off at the second interface in the direction of
        // the reflection, effectively adding to the specular, but this process will repeat itself.
        // Each time the ray exits the surface on the front side after the first bounce,
        // it's multiplied by E^2, and we get: E + E(1-E)^2 + E^3(1-E)^2 + ...
        // This infinite series converges and is easy to simplify.
        // Note: we calculate these bounces only on a single component,
        // since it's a fairly subtle effect.
        E *= 1.0 + pixel.transmission * (1.0 - E.g) / (1.0 + E.g);
#endif

        /* sample the cubemap or screen-space */
#if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
        // when reading from the cubemap, we are not pre-exposed so we apply iblLuminance
        // which is not the case when we'll read from the screen-space buffer
        vec3 t = prefilteredRadiance(ray.direction, perceptualRoughness) * frameUniforms.iblLuminance;
#else
        // compute the point where the ray exits the medium, if needed
        vec4 p = vec4(getClipFromWorldMatrix() * vec4(ray.position, 1.0));
        p.xy = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5);

        // distance to camera plane
        const float invLog2sqrt5 = 0.8614;
        float lod = max(0.0, (2.0 * log2(perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
        vec3 t = textureLod(sampler0_ssr, vec3(p.xy, 0.0), lod).rgb;
#endif

        // base color changes the amount of light passing through the boundary
        t *= pixel.diffuseColor;

        // fresnel from the first interface
        t *= 1.0 - E;

        // apply absorption

Review comment (suggestion):
This `E` is inside the for-loop, meaning the modification for the first channel (red) will affect the second and third channels (green/blue). Not sure if this is intended, but otherwise you may want to make a local copy of E for each iteration as with etaRI and etaRI.

Code after:
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
    for (int i = 0; i < 3; i++) {
        float etaIR = pixel.etaIR[i];
        float etaRI = pixel.etaRI[i];
#else
        float etaIR = pixel.etaIR;
        float etaRI = pixel.etaRI;
#endif

        Refraction ray;

#if REFRACTION_TYPE == REFRACTION_TYPE_SOLID
        refractionSolidSphere(etaIR, etaRI, pixel.thickness, n0, -shading_view, ray);
#elif REFRACTION_TYPE == REFRACTION_TYPE_THIN
        refractionThinSphere(etaIR, pixel.uThickness, n0, -shading_view, ray);
#else
#error invalid REFRACTION_TYPE
#endif

        // compute transmission T
#if defined(MATERIAL_HAS_ABSORPTION)
        vec3 T = min(vec3(1.0), exp(-pixel.absorption * ray.d));
#endif

        // Roughness remapping so that an IOR of 1.0 means no microfacet refraction and an IOR
        // of 1.5 has full microfacet refraction
        float perceptualRoughness = mix(pixel.perceptualRoughnessUnclamped, 0.0,
                saturate(etaIR * 3.0 - 2.0));

        /* sample the cubemap or screen-space */
#if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
        // when reading from the cubemap, we are not pre-exposed so we apply iblLuminance
        // which is not the case when we'll read from the screen-space buffer
        vec3 t = prefilteredRadiance(ray.direction, perceptualRoughness) * frameUniforms.iblLuminance;
#else
        // compute the point where the ray exits the medium, if needed
        vec4 p = vec4(getClipFromWorldMatrix() * vec4(ray.position, 1.0));
        p.xy = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5);

        // distance to camera plane
        const float invLog2sqrt5 = 0.8614;
        float lod = max(0.0, (2.0 * log2(perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
        vec3 t = textureLod(sampler0_ssr, vec3(p.xy, 0.0), lod).rgb;
#endif

        // base color changes the amount of light passing through the boundary
        t *= pixel.diffuseColor;

        // fresnel from the first interface
        t *= 1.0 - E;