
void waterNomal(
    float sunCaustics,
    float rayLength,
    float dY,
    float estEyeDepth,
    vec3 refractedSunVec,
    vec3 ambient,
    vec3 sunColor,
    float sh0,
    inout vec3 absorbance_w,
    inout vec3 vL_w,
    vec3 worldPos,
    vec3 worldPosAbsorb)

{
    //----------------------------------------------------------------------------
    // Initialization and coefficient setup
    //----------------------------------------------------------------------------
    float SdotV = dot(sunPosWorld, normalize(worldPos));

    // Water-specific scattering parameters (assumed defined elsewhere)
    const vec3 mieCoeff = dirtScatterCoef;
    const vec3 scatterCoeff = totEpsilon + mieCoeff;

    // Precomputed phase term and initial caustics factor
    float mieTerm = phaseg(SdotV, 0.4);

    //----------------------------------------------------------------------------
    // Integration loop for volumetric lighting
    //----------------------------------------------------------------------------

    float depthFactor = rayLength / 32.0;
    vec3 viewDir = normalize(worldPos);
    float viewFactor = clamp(viewDir.y, 0.0, 1.0);

    float f_eff = mix(0.25, 0.16, depthFactor) * mix(1.5, 1.0, SdotV);
    float expoBase = mix(1.1, 1.5, depthFactor);
    float steppow_eff = pow(expoBase, f_eff);
    float d_eff = steppow_eff * 2.0 - 2.0;

    float clampedSunElevation = clamp(float(sunElevation), 0.0, 1.0);
    float sunCausticsFactor = sunCaustics * 64.0;
    float shadowFactor_eff = mix(clamp(pow(sh0, f_eff), 0.0, 1.0), sh0 * d_eff, 1.0 - clampedSunElevation);
    float sunCausticsMod_eff = clamp(pow(sunCausticsFactor, f_eff), 0.0, 1.0);

    vec3 sunLight_eff = sunColor * (shadowFactor_eff * sunCausticsMod_eff);
    float eyeDepthDiff_eff = max(estEyeDepth - dY * d_eff, 0.0);
    vec3 sunAtten_eff = exp(-(eyeDepthDiff_eff / abs(refractedSunVec.y)) * totEpsilon * mix(1.0, 0.5, viewFactor));
    vec3 totalMie_eff = mieCoeff * (mieTerm * sunAtten_eff);
    vec3 L_eff = sunLight_eff * totalMie_eff + ambient * mieCoeff;

    //----------------------------------------------------------------------------
    // Optical thickness and final light & absorbance computation
    //----------------------------------------------------------------------------
    vec3 tau = scatterCoeff * rayLength;
    vec3 scatteringFactor = (vec3(1.0) - exp(-tau)) / scatterCoeff;

    vL_w = L_eff * scatteringFactor;
    absorbance_w = exp(-rayLength * totEpsilon);
    if (isEyeInWater == 1)
    {
        absorbance_w = exp(-length(worldPos) * totEpsilon);
    }
}

void waterBasic(
    float rayLength,
    vec3 dVWorld,
    float estEyeDepth,
    vec3 ambient,
    vec3 sunColor,
    inout vec3 absorbance_w,
    inout vec3 vL_w,
    vec3 worldPos,
    vec3 worldPosAbsorb)
{
    // float maxZ = min(rayLength, 32.0) / (rayLength + 1e-8);

    float SdotV = dot(sunPosWorld, normalize(worldPos));

    const vec3 mieCoeff = dirtScatterCoef;
    const vec3 scatterCoeff = totEpsilon + mieCoeff;

    float mieTerm = phaseg(SdotV, 0.4);

    float eyeDepthDiff_eff = max(estEyeDepth - (dVWorld.y * rayLength * 0.1574) / max(length(dVWorld), 1e-6), 0.0);

    vec3 attenuation = exp(-eyeDepthDiff_eff * totEpsilon * 0.75);

    vec3 mieScattering = mieCoeff * (mieTerm * attenuation);

    vec3 sunLight = sunColor * 0.5;
    vec3 L_eff = sunLight * mieScattering + ambient * mieCoeff;

    vec3 tau = scatterCoeff * rayLength;
    vec3 scatteringFactor = (vec3(1.0) - exp(-tau)) / scatterCoeff;

    vL_w = L_eff * scatteringFactor;
    // float foglum = luma(fogColor); //WIP
    // vL_w = mix(luma(vL_w) * (fogColor / foglum), vL_w, clamp(1 - distance(fogColor, vL_w), 0.75, 1));
    float absorbLength = (isEyeInWater == 1) ? length(worldPosAbsorb) : rayLength;
    absorbance_w = exp(-absorbLength * totEpsilon);
}
