Super Raycast - Perfect collision detection against Renderers without colliders.

Features

  • High precision.
  • No collision mesh & no set up needed.
  • SkinnedMeshRenderer (animation) support.
  • Optional Bump Map support for a higher quality collision normal.
  • Optional Height Map support for a higher quality collision point.

Implementation note
SuperRaycast is 400x slower than the built in Unity Physics.Raycast but still fast enough to cast multiple rays against 1000s of objects every Update, even in VR.

If it is slow for you it may be the method you are using. Below are methods available ranked by speed.

  1. Fastest: Maintain an array of Renderers, and call SuperRaycast.Raycast(Ray ray, Renderer targets, out RaycastHitInfo hitInfo);

  2. Fast: Give each object a box collider and call SuperRaycast.PhysicsRaycast, which will use the fast Unity raycast to detect for objects, then it will use SuperRaycast to only test against those objects. It calls transform.GetComponentsInChildren() which is slow, but caches the results, so gets faster.

  3. Medium: SuperRaycast.Raycast(Ray ray, GameObject target, out RaycastHitInfo hitInfo). This will only test against Renderers of this objects children, rather than all Renderers in the scene.

  4. Slowest: SuperRaycast.Raycast(Ray ray, out RaycastHitInfo hitInfo). This calls GameObject.FindObjectsOfType() which is slow if there are a lot of objects.

I’m planning on adding an octree system eventually, which should be faster than all the above methods.

3 Likes

This could using for mobile platforms ? And any have a demo for this ?

I can confirm that as today, the example has errors in mobile. Deleting the multiple objects example and adding the raycast shader to the preloaded assets have make it work in android,

I tried it in a UMI Z PRO and it doesn’t work (it detects collision with the skinned mesh renderer but it can’t give the exact ray position), and it’s too slow in a ZTE BLADE L3.

I hope this helps to move it forward, because it’s a very needed feature.

Thanks for the info FDT, I will try to get around to creating a mobile optimized version eventually.

I’ve submitted v 1.1, which should fix the issues in the multiple objects example. In the new version toggle SuperRaycast.OPENGL = false.

Among other things normal map testing now takes into account the materials normal map scale and I added heighmap support. (Notice the bobbing while cast against a flat plain.)

Hello, this seems to be a very useful tool, how does it works tho? Somebody mentioned something about a raycast shader, will I need to use this shader in the objects I need to hit tests? I have many objects with custom shaders, does this means I will lose the ability to use my own shaders?

What is the performance overheat of this tool?

Thank you in advance for your response, Im looking forward using this tool!

You don’t have to set anything up. Leave all your shaders and materials the way they are. Just call SuperRaycast.Raycast the way you would Physics.Raycast.
The only difference is this will test against renderers, instead of physics meshes.

You can see in the demo video I have like 7 rays casting against an object and am running over >60fps. But I have a decent PC. I’m not sure how well it works on mobile, though there are things I might be able to do to optimize for mobile.

There are two techniques used, one for testing against a single Renderer (MeshRenderer, SkinnedMeshRenderer…) and one for testing against multiple Renderers. The multi renderer Raycasting might be a bit slower. I tested it against 10,000 items at once and it ran at 20-30 fps.

Thanks for your answer, so do you think there wont be any performance overheat when testing hits agains 30k verts meshes?

The complexity of the mesh shouldn’t matter much.
Basically, the objects are drawn to a 2x2 rendertexture. So if your 30k mesh can render to a 1024x760 at runtime speed, rendering to 2x2 should be pretty fast.

After rendering the objects, the biggest bottleneck is reading the RenderTexture. I hope to some day find a faster way to read RenderTexture data. Maybe I have to look at ComputeShaders.

I did a quick stress test comparing it to the Physics.Raycast, but these speeds could be pretty different depending on the gfx card.
100x
Physics Raycast: 0.000495195388793945 ms
Super Raycast: 0.0637741088867188 ms
Physics is 0.0632789134979248 ms faster.

1000x
Physics Raycast: 0.000739097595214844 ms
Super Raycast: 0.493463039398193 ms
Physics is 0.492723941802979 ms faster.

10,000x
Physics Raycast: 0.00943613052368164 ms
Super Raycast: 4.86244487762451 ms
Physics is 4.85300874710083 ms faster.

2 Likes

This is awesome work! I will totally try your asset. Thanks a lot for the analysis

I’m really curious for more information about how it works? Are you doing a texture based raycast? I don’t see how the 2x2 texture would help that much? Wait, are you doing a depth map or something? So you just need the depth of the pixels?

If you could write all the pixels from the individual recasts to one texture, (to then use a compute shader with) I’m sure that would speed things up a fair bit. Though I’m not sure how feasible it would be to assemble multiple camera feeds without the cpu.

Yeah you’re on the right track. Though it doesn’t use multiple camera feeds. I only call camera.Render once per cast.

Reading the RenderTexture takes pretty much %50 of operation time, so a compute shader could really speed things up, unfortunately they don’t work for me in the Linux Editor, even though I have OpenGL 4.1, so I have no way of testing atm.

Hello There!

Just got your asset and was tinkering with it. Any chance to add layermask as an overload? Also a distance limiter like a normal raycast has?
Thanks!

Edit: Also after some testing I can’t seem to get this to hit standard Mesh Renderers. Only Skinned and Terrains. At least doing a simple RayCastHitRenderer.renderer.name always returns null unless its a skinned mesh.

Sure I will add a layermask and max distance option for the next update.

Do you mind showing how you called Raycast so I have a general idea where the problem might be? Thanks.

Ray grabRay = new Ray(transform.position, Vector3.down);
RaycastHit hit;
RaycastHitRenderer hitRend;
if (SuperRaycast.Raycast(grabRay, out hitRend, out hit))

This is for a VR game. I’m raycasting a short distance from the hand, directly down to determine if there is a ledge or climbable object below it to grab onto. If I can get this to work I wont need mesh colliders for everything and that’d be awesome!

I think I found/solved the issue and uploaded it. It shouldn’t take long before the Asset Store approves it.
Max distance, and layer mask filtering are also available now.

Haven’t seen an update on the store. Any idea on an ETA?

Thanks

I’ll DM it to you. Usually updates take a day or two. 7 at most, i think.

Hello

I just got this asset, looks very good. I have a question tho.
I see that in the SkinnedMeshDemo you have to set a target mesh to the hit test. How can I do it if my ray should test different meshes, example a character and its accessories?

Thanks for your help

Another problem, Im getting this error when trying to build:
Player export failed. Reason: Shader error in ‘Teebar/Physics/Super Raycast’: Failed to find parameters of a compiled D3D9 shader (on d3d9)

Hey thanks for the info. That “Failed to find paramaters…” error was solved in 2017.1.05p Unity 2017.1.0p5
It only occurs in some versions of Unity.
I think it’s because they are removing directx 9 support.

For the other question, there are a number of methods that can be called like:

SuperRaycast.PhysicsRaycast(ray): which will cast a ray with Unity’s ray caster, then it will test against the Renderers of the hit objects. So you might add a box collider to your object, so it can be detected by the Unity raycast, and then if contact was made SuperRaycast will run a better test on it.

or

SuperRaycast.Raycast(ray, Renderers[ ]): which will test against an array of Renderers. So for instance when an enemy is spawned you could do

public Renderer[] enemyRenderers = new Renderer[0];
void OnEnemySpawn(){
    ArrayUtility.AddRange(ref enemyRenderers,
    enemy.GetComponentsInChildren<SkinnedMeshRenderer>());
}

or

SuperRaycast.Raycast(ray, GameObject): which will test against all the Renderers in a GameObject.

1 Like