What Does RaycastHit.normal Return?

If I have a horizontal plane (a mesh with collider), and when I do a raycast straight down from above, it returns a normal = Vector 3(0, 1, 0), which is what I’d expect from a horizontal plane. If I tilt that plane away from the horizontal, the X and Z values are no longer zero, reflecting the tilt of the plane, also what I’d expect.

However, if I deform that mesh into a sinusoidal wave pattern (varying the vertices’ Y values), and cast a ray down from above, the normal is still always Vector 3(0, 1, 0), no matter the X and Z coordinates. In other words, even though no part of the mesh is horizontal, the normal returned makes it look like the ray is hitting a horizontal surface.

That’s the part that’s confusing me. How can a ray hitting a non-horizontal object return a normal of Vector3.up? It’s like it’s just returning the rotation of the object.

FWIW, I’m certain the ray is hitting the correct collider and is hitting it at the correct position, since the RaycastHit.point returned always has the correct Y value, which varies with the height of the wave. So the raycast correctly returns the height of the surface, which varies, but not the normal, which should also vary.

The normal is from the collider, not the visible mesh.

–Eric

1 Like

I was pretty sure that in the case of a mesh collider its the triangle normal and not vertex normal. But you say the y value changes but the normal doesnt, this confuses me.As a properly modified mesh of that type would not return flat normals on an angled triangle if my assumptions are correct.

Moving vertices around manually does not automatically update their normals to match the new triangle angles. You need to use:

This only updates the vertex normals, which have no effect on physics.

I made a quick test:

using UnityEngine;
using System.Collections;

public class RCTest : MonoBehaviour {

    private GameObject plane;
    private GameObject caster;
    private void Start()
    {
        plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
        caster = new GameObject("Caster");

        caster.transform.position = Vector3.up * 5f;
        caster.transform.localEulerAngles = new Vector3(90f, 0f, 0f);

        Mesh mesh = plane.GetComponent<MeshFilter>().mesh;
        Vector3[] verts = mesh.vertices;

        for (int i = 0; i < verts.Length; i++)
        {
            float x = verts[i].x;
            float y = Mathf.Sin(verts[i].x) + Mathf.Cos(verts[i].z);
            float z = verts[i].z;
            verts[i] = new Vector3(x, y, z);
        }

        mesh.vertices = verts;
        mesh.RecalculateBounds();
        // mesh.RecalculateNormals(); // No need to do this, because it doesn't affect the physics.
        plane.GetComponent<MeshCollider>().sharedMesh = mesh;
    }

    private void Update()
    {
        Ray ray = new Ray(caster.transform.position, caster.transform.forward);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {

            Debug.DrawLine(ray.origin, hit.point, Color.green);
            Debug.DrawLine(hit.point, hit.point + hit.normal, Color.blue);
        }
    }
}

Drop it on an object in the scene, then move the Caster object around.
Shows me correct triangle normals of a sinusoidal modified mesh where the raycast hits.

1 Like

[QUOTE=

I made a quick test:

       plane.GetComponent<MeshCollider>().sharedMesh = mesh;

[/QUOTE]

That’s the ticket! Thanks. I was doing that once on Start when I created the mesh, but the mesh waveform is updated every frame. Looks like the sharedMesh must be updated too whenever the mesh is changed.

Now I’m having the same trouble again. This time, neither the normals nor the heights (hit.point.y) are correct with respect to the mesh. Everything is exactly the same as it was when working, with one exception. The only change is that I replaced the mesh generated on the fly with one imported from a modeling program. The original mesh was a plane 200 x 200, while the new imported one is 128 x 128. The code that generates the wave is a bit different, but it clearly works as the wave deformation is visible. The mesh collider however seems to remain flat and unchanging. Height of the cay cast is always the same, and normals are always Vector3.up.

At the end of Update, after the vertices have been calculated, it updates the mesh with the new vertices and then sets the shared mesh. Setting the shared mesh every update is what made it work before.

theMesh.vertices = theseVertices;

theMesh.RecalculateBounds();
theMesh.RecalculateNormals();
transform.GetComponent(MeshCollider).sharedMesh = theMesh;

Recalculating bounds and normals wasn’t necessary before, and it makes no difference now.

You may have to make a physics step on that object to make it recalculate it? eg, do something in FixedUpdate().

Thank you! I’ve been looking around at RayCastHit.Normal myself and suddenly someone posts piece of example code up here for me to look at! :slight_smile:

Edit: Yeahhh! More accurate raycasting! :smile:

I’ve tried doing everything in FixedUpdate and LateUpdate as well as Update, that is updating the mesh vertices and updating the shared mesh. I’ve tried updating the vertices in Update and updating the shared mesh in FixedUpdate, and just about every possible combination. I’ve pretty much tried everything every where, and the mesh collider just does not update. The only thing that’s being done to the mesh every frame, updating the vertices Y values, has not changed, yet it no longer works.

Youre assigning the mesh to the collider and not just to the renderer rightk?

Isn’t that what this does?

transform.GetComponent(MeshCollider).sharedMesh = theMesh;

Indeed it is. You are not accidentally assigning the prefab mesh instance then?

I don’t think so. I’m not sure how to tell if I’m assigning the instance.

I get the attached mesh on Start, add a mesh collider, and set the shared mesh. That’s the same mesh that gets manipulated and assigned to the mesh collider every update (isn’t it?). Here is exactly what I’m doing, pretty straightforward:

private var theMesh : Mesh;

function Start () {  
    var mf : MeshFilter = GetComponent(MeshFilter);
    theMesh = mf.mesh;
    gameObject.AddComponent.<MeshCollider>();
    transform.GetComponent(MeshCollider).sharedMesh = theMesh;
}

function Update () {
  
    // do stuff that changes the vertices' Y values here, then assign those changed vertices to the mesh

    theMesh.vertices = theseVertices;

    theMesh.RecalculateBounds();  // this doesn't seem to be necessary
    theMesh.RecalculateNormals();
    transform.GetComponent(MeshCollider).sharedMesh = theMesh;

    // this works the same, makes no difference
    // transform.GetComponent(MeshCollider).sharedMesh.vertices = theseVertices;
    // transform.GetComponent(MeshCollider).sharedMesh.RecalculateNormals();
}

I see in the Inspector when it’s running that the mesh attached to the MeshFilter and the collider mesh are both instances, apparently the same instance. When it’s not running, the mesh is called Plane, and when running it’s called Plane Instance.

If I get rid of line 6, and assign the mesh in the Inspector, then the mesh is called Plane when running and not running. Either way, it makes no difference. Collider vertices and normals are not changing.