vec4 SofronTexture(sampler2D source, vec2 uv, vec2 pixelSize) {
    return texture(source, (floor(uv * pixelSize) + 0.5) / pixelSize);
}

float SofronLight(float light_level, float ambient) {
    // level range [0, 1], ambient range [0, 1]
    float darkness = 1.0 - light_level;
    return (1.0 - darkness) / (darkness * 3.0 + 1.0) * (1.0 - ambient) + ambient;
}

float SofronLinearFogFactor(float vertexDistance, float fogEnd) {
    float fogStart = fogEnd * 0.25; // todo: for overworld, 0 for nether/end
    if (vertexDistance <= fogStart) {
        return 0.0;
    } else if (vertexDistance >= fogEnd) {
        return 1.0;
    }
    return clamp((vertexDistance - fogStart) / (fogEnd - fogStart), 0.0, 1.0);
}
float SofronExpFogFactor(float vertexDistance, float density) {
    return 1.0 - clamp(exp(-density * vertexDistance), 0.0, 1.0);
}
vec4 SofronApplyFog(vec4 color, float vertexDistance, float renderDistance, float environmentDistance, vec4 fogColor) {
    if (fogColor.a <= 0.0) return color;
    float factor = 0.0;
    if (environmentDistance > 96.0) {
        factor = SofronLinearFogFactor(vertexDistance, renderDistance);
    } else {
        bool inLava = fogColor.r >= 0.6 && fogColor.g <= 0.1 && fogColor.b == 0.0;
        float density = inLava ? 2.0 : 0.1; // inLava ? 2.0 : (inWater ? 0.1 : 1.0);
        factor = SofronExpFogFactor(vertexDistance, density);
    }
    return vec4(mix(color.rgb, fogColor.rgb, factor), color.a);
}
float SofronLinearSkyFogFactor(float vertexDistance, float fogEnd) {
    if (fogEnd <= 64.0) return 1.0; // no sky renders on short or tiny in beta
    float fogStart = 0.0;
    fogEnd = fogEnd * 0.8;
    if (vertexDistance <= fogStart) {
        return 0.0;
    } else if (vertexDistance >= fogEnd) {
        return 1.0;
    }
    return clamp((vertexDistance - fogStart) / (fogEnd - fogStart), 0.0, 1.0);
}
vec4 SofronApplySkyFog(vec4 color, float vertexDistance, float renderDistance, float environmentDistance, vec4 fogColor) {
    if (fogColor.a <= 0.0) return color;
    float factor = SofronLinearSkyFogFactor(vertexDistance, min(renderDistance, environmentDistance));
    return vec4(mix(color.rgb, fogColor.rgb, factor), color.a);
}
