float GetApproxDistance(float depth) {
    return near * far / (far - depth * far);
}

vec2 DoRefraction(inout vec3 color, inout float z0, inout float z1, vec3 viewPos, float lViewPos) {
    // Prep
    int matId = int(texelFetch(colortex6, texelCoord, 0).g * 255.1);
    bool isWater = (matId == 241);
    bool isGlass = (matId == 242);
    if (!isWater && !isGlass) return texCoord.xy;

    float fovScale = gbufferProjection[1][1];

    vec3 playerPos = ViewToPlayer(viewPos.xyz);
    vec3 worldPos = playerPos.xyz + cameraPosition.xyz;

    vec2 refractNoise;

    if (isWater) {
        // Original water: animated noise-based warp
        vec2 worldPosRM = worldPos.xz * 0.02 + worldPos.y * 0.01 + 0.01 * frameTimeCounter;
        refractNoise = texture2DLod(noisetex, worldPosRM, 0.0).rb - vec2(0.5);
        refractNoise *= WATER_REFRACTION_INTENSITY * fovScale / (3.0 + lViewPos);
        #if WATER_STYLE < 3
            refractNoise *= 0.015;
        #else
            refractNoise *= 0.02;
        #endif
    } else {
        // Glass: small, stable ripple tied to world position (not screen
        // position), so the surface reads as glass without warping into a
        // lens/fisheye shape as the camera moves or the view angle changes.
        vec2 worldPosRM = worldPos.xz * 0.08 + worldPos.y * 0.04;
        vec2 noiseOffset = texture2DLod(noisetex, worldPosRM, 1.0).rb - vec2(0.5);
        refractNoise = noiseOffset * 0.0025;
        refractNoise *= fovScale / (1.5 + lViewPos * 0.3);
    }

    // Check
    float approxDif = GetApproxDistance(z1) - GetApproxDistance(z0);
    refractNoise *= clamp(approxDif, 0.0, 1.0);

    vec2 refractCoord = texCoord.xy + refractNoise;

    // For glass accept both water and glass IDs on the check coord
    int checkId = int(texture2D(colortex6, refractCoord).g * 255.1);
    if (isWater && checkId != 241) return texCoord.xy;
    if (isGlass && checkId != 242) return texCoord.xy;

    float z0check = texture2D(depthtex0, refractCoord).r;
    float z1check = texture2D(depthtex1, refractCoord).r;
    float approxDifCheck = GetApproxDistance(z1check) - GetApproxDistance(z0check);
    refractNoise *= clamp(approxDifCheck, 0.0, 1.0);

    // Sample
    refractCoord = texCoord.xy + refractNoise;
    color = texture2D(colortex0, refractCoord).rgb;
    z0 = texture2D(depthtex0, refractCoord).r;
    z1 = texture2D(depthtex1, refractCoord).r;
    return refractCoord;
}