#if !defined RAYTRACER
#define RAYTRACER
    float ascribeDepth(in float depth, in float amount) {
        depth = screenSpaceToViewSpace(depth, gbufferProjectionInverse);
        depth *= 1.0 + amount;
        return viewSpaceToScreenSpace(depth, gbufferProjection);
    }

    bool raytraceIntersection(in sampler2D depthtexture, in vec3 startPosition, in vec3 rayDirection, out vec3 hitPosition, in uint stride, in float depthLeniency, in bool isDeferred) {
        hitPosition = startPosition;
        startPosition = screenSpaceToViewSpace(startPosition, gbufferProjectionInverse);

        vec3 rayStep  = startPosition + abs(startPosition.z) * rayDirection;
             rayStep  = viewSpaceToScreenSpace(rayStep, gbufferProjection) - hitPosition;
             rayStep *= minof((step(0.0, rayStep) - hitPosition) / rayStep);

        hitPosition.xy *= viewSize;
        rayStep.xy     *= viewSize;

        rayStep /= maxof(abs(rayStep.xy));

	    float dither = floor(stride * fract(fract(frameCounter * (1.0 / phi)) + bayer128(gl_FragCoord.st)) + 1.0);

        vec3 steps = (step(0.0, rayStep) * vec3(viewSize - 1.0, 1.0) - hitPosition) / rayStep;
        steps.z += float(stride);
        float tMax = min(minof(steps), maxof(viewSize));

        vec3 rayOrigin = hitPosition;

        float ascribeAmount = depthLeniency * float(stride) * viewPixelSize.y * gbufferProjectionInverse[1].y;

        bool intersected = false;
        float t = dither;
        while(t < tMax && !intersected) {
            float stepStride = t == dither ? dither : float(stride);

            hitPosition = rayOrigin + t * rayStep;

            float maxZ = hitPosition.z;
            float minZ = hitPosition.z - stepStride * abs(rayStep.z);

            float depth = texelFetch(depthtexture, ivec2(hitPosition.xy), 0).r;
            float ascribedDepth = ascribeDepth(depth, ascribeAmount);

            intersected = maxZ >= depth && minZ <= ascribedDepth;
            intersected = intersected && depth < 1.0;
            if(saturate(hitPosition.xy) == hitPosition.xy) { intersected = false; break; }
            if(!intersected) { t += float(stride); }
        }

        if(intersected) {
            bool refinementIntersection = true;
            float refinementStride = stride;
            for(int i = 0; i < findMSB(stride); ++i) {
                t += (refinementIntersection && t > 0.0 ? -1.0 : 1.0) * (refinementStride *= 0.5);
                hitPosition = rayOrigin + t * rayStep;

                float maxZ = hitPosition.z;
                float minZ = hitPosition.z - stride * abs(rayStep.z);

                float depth = texelFetch(isDeferred ? depthtex0 : depthtex1, ivec2(hitPosition.xy), 0).r;
                float ascribedDepth = ascribeDepth(depth, ascribeAmount);

                refinementIntersection = maxZ >= depth && minZ <= ascribedDepth;
                intersected = intersected && depth < 1.0;
            }
        }

        hitPosition.xy *= viewPixelSize;

        return intersected;
    }
#endif