Search Unity

Normal from terraindata

Discussion in 'Scripting' started by Alieno79, Jan 20, 2021.

  1. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    Hi everyone,
    I can't read the Normal of a terrain
    Making a raycast I have the correct result, reading it from terraindata gives me an incorrect value.
    I attach code and screenshots

    In update :

    Code (CSharp):
    1.         RaycastHit hit;
    2.         Ray raggio = Camera.main.ScreenPointToRay(Input.mousePosition);
    3.         Vector3 posiz = new Vector3(36f, 15f, 7f);
    4.         if (Physics.Raycast(posiz + (Vector3.up*5),Vector3.down, out hit, Mathf.Infinity, MyGLB_E.layer_Terreno))
    5.         {
    6.             Terrain at = Terrain.activeTerrain;
    7.             float normalizedX, normalizedY; Vector3 normale;
    8.             hPoint = hit.point;
    9.             normalizedX = posiz.x / at.terrainData.size.x ;
    10.             normalizedY = posiz.z / at.terrainData.size.z ;
    11.             normale = at.terrainData.GetInterpolatedNormal(normalizedX, normalizedY);
    12.             hpointAngolo = at.terrainData.GetSteepness(normalizedX, normalizedY);          
    13.             hString = "TERRAINDDATA:\n\rSP : " +  hpointAngolo.ToString() + "\n\rUP : " + Vector3.Angle(Vector3.up,normale ).ToString() + "\n\rDW : " + Vector3.Angle(normale, Vector3.down).ToString() + "\n\rFW : " +  Vector3.Angle(normale, Vector3.forward).ToString() + "\n\rBW : " + Vector3.Angle(normale, Vector3.back).ToString() + "\n\rRG : " + Vector3.Angle(normale, Vector3.right).ToString() + "\n\rLF : " + Vector3.Angle(normale, Vector3.left).ToString() + "\n\rNR : " +  normale;
    14.             hN = normale;
    15.             normale = hit.normal;
    16.             hN0 = normale;
    17.             hString0 = "RAYCAST\n\rUP : " +  Vector3.Angle(normale, Vector3.up).ToString() + "\n\rDW : " + Vector3.Angle(normale, Vector3.down).ToString() + "\n\rFW : " + Vector3.Angle(normale, Vector3.forward).ToString() + "\n\rBW : " + Vector3.Angle(normale, Vector3.back).ToString() + "\n\rRG : " + Vector3.Angle(normale, Vector3.right).ToString() + "\n\rLF : " + Vector3.Angle(normale, Vector3.left).ToString() + "\n\rNR : " + hit.normal;
    18.  
    19.         }
    onGizmo :
    Code (CSharp):
    1.         GUIStyle style = new GUIStyle(GUI.skin.label);
    2.         style.alignment = TextAnchor.MiddleLeft;
    3.         Handles.Label(hPoint, hString0, style);
    4.         Handles.Label(hPoint + Vector3.right, hString, style);
    5.         Gizmos.color = Color.green;
    6.         Gizmos.DrawLine(hPoint,hPoint + ( hN * 1));
    7.         Gizmos.color = Color.red;
    8.         Gizmos.DrawLine(hPoint, hPoint + (hN0 * 1));
    risultato :

    Immagine.jpg

    tanks for your help
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,752
    I'm not sure what to tell you here. Perhaps just use the correct one you get from Raycast?

    AFAIK, TerrainData does not contain normal data. The heightmap is used to construct geometry, and from that you can get the normal, which is what raycast does. I suppose you could recreate it yourself from the heightmap data, but to do that you would need to know the heightmap-to-mesh code that Unity uses, or else yours would not match.
     
  3. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    Sorry, but then the terrainData.GetInterpolatedNormal function that the unity guide exposes, what is it for?
    As I understand it should (passing the normalized coordinates) give the value of the normal.
    I have seen some examples, and they use it for this very reason, but I do not understand why things should never go as shown and used by others.:(:(:(
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,752
    The docs say that it returns normalized quantities and ONLY the X,Z directions. You would have to experiment to see what a 2-coordinate "normalized" value even means in a 3D world. Is it just the slope of the raw heightmap data? Does it take into account scale of the terrain? It doesn't say. Are you scaling appropriate to the presentation size of your terrain? Same goes for GetSteepness... those return normalized quantities too.

    If others are using it, use their code. Otherwise you have to figure out what the data coming back even means in your space.
     
    Joe-Censored likes this.
  5. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    I tried using the same code, but nothing

    The scale is 1, the coordinates 0,0,0, the dimensions 500,500,500

    However I have seen they use the normal and stepness to keep the gameobjects parallel to the ground under them (therefore in a 3D environment)

    I would not like to use raycasts as multiplied by the various gameobjects would result in a heavy calculation, while accessing the data already contained in the map eases the calculations a lot

    I just need to know if I have an inclination greater than 35 degrees in a certain point

    However I look for more information and help, I hope to resolve.
    Thank you very much for your interest and help! :)
     
  6. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Was playing around with this and got a basic setup working. Not sure if it helps the OP, but hopefully is useful to someone.

    I'm confused as to why the height values in this are being scaled. What I mean is:

    --the height values for the gizmo drawing are about 5X out from where they should be: at (0,0) they are correct, at (10,5) they show the height that's actually at (2,1), etc.
    --the normal vectors are correct, as in: they point in the right direction relative to the terrain's slope.

    The screenshot shows it in "corrected" mode, where I've manually scaled where the heights are grabbed from so they line up with the sloping (pretty much). What I don't understand is WHY it works like this, seems like the scaling shouldn't be needed!

    Attach this script to your terrain GameObject (keep it small for better interaction) and give it a go:


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class forumNormals : MonoBehaviour
    6. {
    7.     Terrain terrain; // recommend not a huge terrain, for smoother interaction
    8.  
    9.     [Range(0.1f, 20)]
    10.     public float rayScale = 2; // length of gizmo ray
    11.  
    12.     void Start()
    13.     {
    14.         terrain = GetComponent<Terrain>();
    15.     }
    16.     void OnDrawGizmos()
    17.     {
    18.         Vector3 terrainSize = terrain.terrainData.size;
    19.  
    20.         for (int i = 0; i < terrainSize.x; i += 2) // use steps of more than 1 for smoother interaction in editor
    21.         {
    22.             for (int k = 0; k < terrainSize.z; k += 2)
    23.             {
    24.                 // not sure why the gizmo y-heights are scaled out from 0,0 (about 5X) when doing it this way.
    25.                 // Uncomment this and comment the second one to see:
    26.                 //Vector3 pivot = new Vector3(i, terrain.terrainData.GetHeight(i, k), k);
    27.  
    28.                 // this way fixes it (pretty much)--WHY??
    29.                 Vector3 pivot = new Vector3(i, terrain.terrainData.GetHeight(i * 5, k * 5), k);
    30.  
    31.                 float x = pivot.x / terrainSize.x;
    32.                 float z = pivot.z / terrainSize.z;
    33.                 Vector3 interpolatedNormal = terrain.terrainData.GetInterpolatedNormal(x, z);
    34.  
    35.                 GUI.color = Color.blue;
    36.                 Gizmos.DrawSphere(pivot, 0.2f);
    37.                 Gizmos.color = Color.cyan;
    38.                 Gizmos.DrawRay(pivot, interpolatedNormal * rayScale);
    39.             }
    40.         }
    41.     }
    42. }
    43.  
     

    Attached Files:

  7. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    First of all, thank you for your reply
    I tried, but the results are weird ..
    I took 2 screenshots
    The first with the multiplier * 5, the second without a multiplier.
    However, the limitation of not having precise points through the getHeight I think affects, as and for this reason in the second case the pivots are far from contact with the ground .. maybe ..
    Immagine.jpg


    Immagine2.jpg

    However I try to deepen
    Thank you very much
     
  8. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    absurd! if I try to indicate the coordinates myself, it returns to the same point as before ...
    Immagine.jpg
     
  9. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    I figured out the issue with mine---I had set the Terrain width and length to 100 x 100 so things were smoother in the Inspector. I didn't change the heightmap resolution to match! DOH. Makes perfect sense that 5X *almost* fixed it...default heightmap resolution is 513.

    So, if you do change the terrain height/width, recommend using a power of 2, then adjust heightmap resolution to match + 1. 128x128 terrain --> 129x129 heightmap (these are the vertices).

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class forumNormals : MonoBehaviour
    6. {
    7.     Terrain terrain; // recommend not a huge terrain, for smoother interaction
    8.     // When resizing terrain (width and length), use a power of 2 for each side: 128, 512 (default), etc.
    9.     // After resizing, set heightmap resolution to match: Side+1 in each dimension, so
    10.     // 128x128 map => 129X129 Heightmap (these are the vertices, so one more is needed)
    11.  
    12.     [Range(0.1f, 20)]
    13.     public float rayScale = 2; // length of gizmo ray
    14.  
    15.     void Start()
    16.     {
    17.         terrain = GetComponent<Terrain>();
    18.     }
    19.     void OnDrawGizmos()
    20.     {
    21.         Vector3 terrainSize = terrain.terrainData.size;
    22.  
    23.         for (int i = 0; i < terrainSize.x; i += 10) // use steps of more than 1 for smoother interaction in editor
    24.         {
    25.             for (int k = 0; k < terrainSize.z; k += 10)
    26.             {
    27.                 Vector3 pivot = new Vector3(i, terrain.terrainData.GetHeight(i, k), k);
    28.  
    29.                 float x = pivot.x / terrainSize.x;
    30.                 float z = pivot.z / terrainSize.z;
    31.                 Vector3 interpolatedNormal = terrain.terrainData.GetInterpolatedNormal(x, z);
    32.  
    33.                 GUI.color = Color.blue;
    34.                 Gizmos.DrawSphere(pivot, 0.2f);
    35.                 Gizmos.color = Color.cyan;
    36.                 Gizmos.DrawRay(pivot, interpolatedNormal * rayScale);
    37.             }
    38.         }
    39.     }
    40. }
    41.  
     
    Bunny83 likes this.
  10. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    @Alieno79 , check your terrain size versus heightmap resolution. Default terrain size is 1000x1000, heightmap res is 513x513, so there's interpolation. Not an issue unless we're trying something like showing normals on every texel (at least as I understand it). Try matching the size to heightmap res + 1 and see if the points line up on the ground. Mine look correct, this is 512x512 and 513x513:
     

    Attached Files:

  11. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    Thank you so much, I'll try today and I'll let you know!
     
  12. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    Nothing to do, these are the settings, but I don't understand what I'm wrong Immagine.jpg
     
  13. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Try 1024x1024 for the terrain and 1025x1025. Should shift the points ever so slightly.
     
  14. Alieno79

    Alieno79

    Joined:
    Oct 11, 2015
    Posts:
    19
    i try it but result is the same.. at integer coordinates it's ok, but in float coordinates.. is it wrong..
    i think must use raycast

    thanks you all
     
  15. dmtrjdenic2

    dmtrjdenic2

    Joined:
    Jul 5, 2020
    Posts:
    1
    I know I am a bit late but Ii anybody else is having this issue, I managed to find the solution. Here is the code :

    Code (CSharp):
    1. public Vector3 CalculateTerrainNormal(Vector3 terrainPosition)
    2.         {
    3.             var offset = 0.1f;
    4.  
    5.             var heightLeft = _terrain.SampleHeight(terrainPosition + Vector3.left * offset);
    6.             var heightRight = _terrain.SampleHeight(terrainPosition + Vector3.right * offset);
    7.             var heightForward = _terrain.SampleHeight(terrainPosition + Vector3.forward * offset);
    8.             var heightBack = _terrain.SampleHeight(terrainPosition + Vector3.back * offset);
    9.  
    10.             var tangentX = new Vector3(2.0f * offset, heightRight - heightLeft, 0).normalized;
    11.             var tangentZ = new Vector3(0, heightForward - heightBack, 2.0f * offset).normalized;
    12.  
    13.             var normal = Vector3.Cross(tangentZ, tangentX).normalized;
    14.          
    15.             return normal;
    16.         }
    terrainPosition is the world position you are checknig for a normal