Visual Raycasts (non-physics)

I am writing some editor tools and one thing that’d be really neat to have is raycasts against renderers. I would like to be able to cast a ray and get the point on the surface of the first mesh triangle that I hit - in my case I wouldn’t even care about what GameObject I hit, but of course that’d also be nice.

It seems that people can do it via detours, e.g., by creating a mesh collider and using physics after all, or using some renderer trickery, or writing their own raycasts against meshes from scratch. But why would I do so much work for such a basic task?

The Unity Editor already has this functionality out of the box: when I drag any mesh (static or skinned) into the editor, I can click it and it gets selected - the most basic of things. This is very precise, so I’m pretty certain raycasts of some sort are being used. It also works for any rendering pipeline, which can’t be said about certain assets on the asset store.

So my question: is there an API for this functionality?
If there is, I cannot find it and any help would be appreciated. If there isn’t, I wonder why there isn’t?

looks like this could work,

(found from here UnityCsReference/Editor/Mono/SceneView/SceneViewPicking.cs at f50ab75c509cab05254e0ff2f06eb74f5ecd30da · Unity-Technologies/UnityCsReference · GitHub )

here’s one using camera depth texture (should work in build also)
https://github.com/Jonny10/Graphics-Raycast

asset store has some that check mesh triangles vs ray intersection or so.

// adaptation of
// https://discussions.unity.com/t/475294
//
// Moller/Trumbore intersection algorithm
static private bool rayTriangleIntersection_impl(
  in Vector3 rayOrigin, in Vector3 rayDirection,
  in Vector3 point, in Vector3 edge1, in Vector3 edge2,
  out float distance, out Vector2 baryCoord,
  bool backfaceCulling = true) {

  baryCoord = VectorEx.Vec2NaN;
  distance = Mathf.Infinity;

  var p = rayDirection.CrossWith(edge2);
  var det = edge1.DotWith(p);

  if(backfaceCulling) {
    if(det.IsZeroOrNegative()) return false;

    var t = point.ToPoint(rayOrigin);
    var u = t.DotWith(p);
    if(!u.IsWithin(0f, det)) return false; // if(u < 0f || u > det)

    var q = t.CrossWith(edge1);
    var v = rayDirection.DotWith(q);
    if(!v.IsWithin(0f, det - u)) return false; // if(v < 0f || u + v > det)

    var invDet = 1f / det;
    distance = edge2.DotWith(q) * invDet;
    baryCoord.x = u * invDet;
    baryCoord.y = v * invDet;

  } else {
    if(det.IsCloseToZero()) return false;
    var invDet = 1f / det;

    var t = point.ToPoint(rayOrigin);
    var u = t.DotWith(p) * invDet;
    if(!u.IsWithin01()) return false; // if(u < 0f || u > 1f)

    var q = t.CrossWith(edge1);
    var v = rayDirection.DotWith(q) * invDet;
    if(!v.IsWithin(0f, 1f - u)) return false; // if(v < 0f || u + v > 1f)

    distance = edge2.DotWith(q) * invDet;
    baryCoord.x = u;
    baryCoord.y = v;
  }

  return true;
}

static public bool RayTriangleIntersection(
  in Vector3 rayOrigin, in Vector3 rayDirection,
  in Vector3 point1, in Vector3 point2, in Vector3 point3,
  out float distance, out Vector2 baryCoord, bool backfaceCulling = true) {

  var edge1 = point1.ToPoint(point2);
  var edge2 = point1.ToPoint(point3);

  return rayTriangleIntersection_impl(
    rayOrigin, rayDirection,
    point1, edge1, edge2,
    out distance, out baryCoord,
    backfaceCulling
  );
}

static public bool RayTriangleIntersection(
  Ray ray,
  in (Vector3 a, Vector3 b, Vector3 c) triangle,
  out float distance, out Vector3 hitPoint,
  bool backfaceCulling = true) {

  var edge1 = triangle.a.ToPoint(triangle.b);
  var edge2 = triangle.a.ToPoint(triangle.c);

  bool hit = rayTriangleIntersection_impl(
    ray.origin, ray.direction,
    triangle.a, edge1, edge2,
    out distance, out var bary,
    backfaceCulling
  );

  hitPoint = triangle.a + bary.x * edge1 + bary.y * edge2;
  return hit;
}

In case you need it.

Vec2NaN means new Vector2(float.NaN, float.NaN)
a.ToPoint(b) means (b - a)
a.IsCloseToZero() means Mathf.Abs(a) < 1E-6f
a.IsZeroOrNegative() means a < 0f || a.IsCloseToZero()

the rest is obvious, I hope.

1 Like

Yep, I eventually found that one. Interestingly it gives you the material that was clicked, which suggests exactly what I supposed: all the tools are there, just not exposed. Why is there no version that gives you a HitInfo with all the information?

@orionsyndrome thanks, but my problem wasn’t that I couldn’t do it. Still good for others for reference I guess. My question was why do I have to cope with that and write lots of code I’ll have to maintain when it’s all there in Unity already and all they’d have to do is expose it?

1 Like