I would use a combined type and I'll tell you why: because computation of a value should return the value, not mutate a bunch of variables. Mutating a bunch of variables doesn't scale once you need more than one of them mutated. Suppose you want a thousand of these things:
IEnumerable<Ray> rays = GetAThousandRays();
var intersections = from ray in rays
where Intersect(ray, out distance, out normal)
orderby distance ...
Executing the query is now repeatedly mutating the same two variables. You're ordering based on a value that is being mutated. This is a mess. Don't make queries that mutate things; it is very confusing.
What you want is:
var intersections = from ray in rays
let intersection = Intersect(ray)
where intersection.Intersects
orderby intersection.Distance ...
No mutation; manipulate a sequence of values as values not as variables.
I also would be inclined to maybe get rid of that Boolean flag, and make the value an immutable struct:
// returns null if there is no intersection
Intersection? Intersect(Ray ray) { ... }
struct Intersection
{
public double Distance { get; private set; }
public Vector3 Normal { get; private set; }
public Intersection(double distance, Vector3 normal) : this()
{
this.Normal = normal;
this.Distance = distance;
}
}