Search Unity

Get Material From Raycast

Discussion in 'Scripting' started by kirua-sama, Jul 1, 2010.

Thread Status:
Not open for further replies.
  1. kirua-sama

    kirua-sama

    Joined:
    Mar 21, 2010
    Posts:
    41
    Hye !

    I have a problem that I cannot resolved.
    To try to get a material from a rayCast.

    So, I'm doing this :

    Code (csharp):
    1. var ray : Ray;
    2.     var hit : RaycastHit;
    3.  
    4.     //Calcul du point de création de l'objet sur le plan: indiqué par la souris
    5.     ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    6.     if (!Physics.Raycast (ray.origin, ray.direction, hit, 100))
    7.         return;
    8.    
    9.     var renderer : Renderer = hit.collider.renderer;
    10.     var meshCollider = hit.collider as MeshCollider;
    11.                
    12.     if (renderer == null || renderer.sharedMaterial == null || renderer.sharedMaterial.mainTexture == null || meshCollider == null)
    13.         return;
    14.  
    15.                    
    16.     Debug.Log("-------------------- I'm using " + renderer.material.name + " material(s)");
    However I cannot find a solution when I have several materials for just one collider.

    How can I get The material wihich has been it by the rayCast ?

    Thanks.
     
    Necronomicron likes this.
  2. zeve

    zeve

    Joined:
    Aug 10, 2009
    Posts:
    12
    The raycast hits a triangle in your object (collider). If your object has several materials you should check to which material the triangle belongs to.
    - you find out the index of the triangle that was hit ( RaycastHit.triangleIndex )
    - iterate through every submesh of the object ( Mesh.subMeshCount )
    - for every submesh you check if it contains the triangle that was hit (using Mesh.GetTriangles), and if yes you remember the submesh index
    - the correct material you want is the material that is associated with that submesh

    Try this:

    Code (csharp):
    1. var ray : Ray;
    2.    var hit : RaycastHit;
    3.  
    4.    ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    5.    if (!Physics.Raycast (ray.origin, ray.direction, hit, 100))
    6.       return;
    7.    
    8.    var renderer : Renderer = hit.collider.renderer;
    9.    var meshCollider = hit.collider as MeshCollider;
    10.            
    11.    if (renderer == null || renderer.sharedMaterial == null || renderer.sharedMaterial.mainTexture == null || meshCollider == null)
    12.       return;
    13.  
    14. // now starts my code
    15. var triangleIdx : int = hit.triangleIndex;
    16. var mesh : Mesh = hit.collider.gameObject.GetComponent (MeshFilter).mesh;
    17. var subMeshesNr : int = mesh.subMeshCount;
    18. var materialIdx : int = -1;
    19.  
    20. for(var i=0,i<subMeshesNr ; i++) {
    21.     var tr = mesh.GetTriangles(i);
    22.     for(var j=0;j<tr.length ; j++) {
    23.         if (tr[j] == triangleIdx) {
    24.             materialIdx = i;
    25.             break;
    26.         }
    27.     }
    28.     if (materialIdx != -1) break;
    29. }
    30.  
    31. if (materialIdx != -1)
    32.    Debug.Log("-------------------- I'm using " + renderer.materials[materialIdx].name + " material(s)");  
     
    SamFernGamer4k likes this.
  3. kirua-sama

    kirua-sama

    Joined:
    Mar 21, 2010
    Posts:
    41
    It's not working, but It's quit weird. It seems to be more or less random. I'll try to work it out well.

    Actually the problem comes from the rayCast. When I click twice on an object sometimes the rayCast give me the object behind the one I clicked ... It must be a problem of layer, but it's still very random as error.
     
  4. vrdave

    vrdave

    Joined:
    Jan 28, 2013
    Posts:
    2
    I know this post is old, but I came across it when attempting a similar situation. It's not a problem of being random, it's a matter of how the triangle index is being used and the assumptions being made. You cannot assume that GetTriangles will return an array that can be used with the triangle index. Instead, you have to do your lookup on the mesh.triangles array instead. This will give you the vertex number you want to do your lookup against the result of GetTriangles. The other thing to keep in mind is that a triangle is made up of 3 vertices, which means you have to correlate the vertices in the triangles array with the vertices in the submesh triangle array, looping based on multiples of 3 instead of 1.

    Code (JavaScript):
    1. var ray : Ray;
    2. var hit : RaycastHit;
    3. ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    4. if (!Physics.Raycast (ray.origin, ray.direction, hit, 100)) {
    5.     return;
    6. }
    7.    
    8. var renderer : Renderer = hit.collider.renderer;
    9. var meshCollider = hit.collider as MeshCollider;
    10.          
    11. if (renderer == null || renderer.sharedMaterial == null || renderer.sharedMaterial.mainTexture == null || meshCollider == null) {
    12.     return;
    13. }
    14.  
    15. var materialIdx : int = -1;
    16.  
    17. var mesh : Mesh = meshCollider.sharedMesh;
    18. var triangleIdx : int = hit.triangleIndex;
    19. var lookupIdx1 : int = mesh.triangles[triangleIdx * 3];
    20. var lookupIdx2 : int = mesh.triangles[triangleIdx * 3 + 1];
    21. var lookupIdx3 : int = mesh.triangles[triangleIdx * 3 + 2];
    22.  
    23. var subMeshesNr : int = mesh.subMeshCount;
    24. for(var i=0,i<subMeshesNr ; i++) {
    25.     var tr = mesh.GetTriangles(i);
    26.     for(var j=0;j<tr.length ; j += 3) {
    27.         if (tr[j] == lookupIdx1 && tr[j+1] == lookupIdx2 && tr[j+2] == lookupIdx3) {
    28.             materialIdx = i;
    29.             break;
    30.         }
    31.     }
    32.     if (materialIdx != -1) break;
    33. }
    34. if (materialIdx != -1) {
    35.    Debug.Log("-------------------- I'm using " + renderer.materials[materialIdx].name + " material(s)");
    36. }
    I hope this helps someone else who comes across this thread. Sorry if the JS doesn't compile immediately, I'm a little rusty. If you want more information about how this all works, the documentation is pretty good: http://docs.unity3d.com/ScriptReference/Mesh-triangles.html, http://docs.unity3d.com/ScriptReference/RaycastHit-triangleIndex.html
     
  5. Deleted User

    Deleted User

    Guest

    FWIW, the above method is abysmal in both the terms of performance and logic. It's best to use the fact that submeshes, corresponding to materials/textures are laid out linearly, so you can just use https://docs.unity3d.com/ScriptReference/Rendering.SubMeshDescriptor-indexStart.html (e.g. divided by 3 if it's a tri mesh) to get the ranges of tri ordinals that are used by each and every submesh (i.e. material). Instead of iterating through each vertex (performance drop will be serious), you just iterate through every material. Alternatively, create a lookup <int,Texture> dictionary on init and populate it using the aforementioned indexStart data. In my case, it reduced the timings of the method from milliseconds to microseconds (i.e. basically just the raycast and related calls)
     
    SamFernGamer4k and tomekkie2 like this.
  6. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Doesn't really matter at this point since it's UnityScript code, which is no longer supported.
     
  7. Deleted User

    Deleted User

    Guest

    It does matter, because the algorithms can be trivially ported to C# or any other language. Hell, the syntax changes are so minor with C# and ES being so similar nowadays (it's enough to basically drop the TS-ish type hints), that I don't understand what you're trying to say at all, because it simply doesn't make any sense to me in this particular context. What are you trying to achieve by your post? What exactly is your input? How are you helping anyone? Don't you have anything better to do with your life than spread FUD around?

    Do you know the quote from Abe Lincoln about remaining silent?
     
  8. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Relax, I did not intend to insult you, if that's how you took it.

    Rather just pointing out that this thread is very old, and the responses are pretty irrelevant for modern use.
    I don't see why UnityScript being easy to convert to C# matters here - an answer being written in UnityScript alone should be enough to indicate that it's outdated & shouldn't be used.
     
  9. Deleted User

    Deleted User

    Guest

    I don't feel insulted. I feel that logic and common sense were insulted.

    You're pointing at a thing that's obvious to every creature that had developed the ability to read.

    They are relevant, even if they are wrong-ish. Try googling "unity get material from raycast", and consider the results. (hint: for my query, 3 top answers are: this one, one that explicitly links to this one, and one describing the very same absurd solution as proposed here)

    You can hardly get any more relevant.

    I can't help you then. Improving cognitive or perceptual performance of humans is outside of my abilities.

    Seldom do I read things that are this absurd. Non sequitur. You could as well say that A* algorithm is outdated, just because it was written in 1968. What you write simply does not make any sense. Things that are old can be outdated, but this correlation does not imply causation by itself.

    Again, why are you doing this? Who's your desired audience?
     
    SamFernGamer4k likes this.
  10. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Alright then, I'll shut up. Relax.
     
    austephner and SamFernGamer4k like this.
Thread Status:
Not open for further replies.