Extracting RGB component data from a textured plane positioned below a 3D terrain

I have a simple model that I am trying to test as a proof of concept.

1) Terrain located at 0,0,0 2) Textured plane at 0,-10,0 (below the terrain and unseen) currently greyscale; but would like to read RGB eventually. Sized to match extents of terrain. Lets call it a 'data-field' (literally).

The character controller neeeds to extract data from the data-field (raycast?) and 'print it'.

Extracted data might contain useful information for AI use (e.g. good place for cover; look up; wait here if under fire) so we would be returning multiple 0-255 rankings from multiple data-fields for subsequent processing by the AI. Obviously RGB data-planes have 3 x the bang for the same buck, if you separate the components!

This has got to be better than waypoints as a basic wayfinding strategy; websearch 'isovist fields' if you want more info on this theory applied to permeability/pathfinding.

In principle, this is an opportunity to generate complex response data from multiple painted planes (which could be layered off...???/unrendered???)

This is the beginning of potential spatial data discussion, but my brain is not there yet.

G

Here's one way ...

  • associate a Collider with your textured plane
  • use Collider.Raycast to cast vertical rays (direction = Vector3.down) down into the plane from the world position of the character
  • use the textureCoord of the raycast hit and feed it into Texture2D.GetPixel to read the RGB value from the texture

EDIT: The following script seems to do the trick.

To use it ...

  1. Save the script as DataField.cs in your project.
  2. Select Game Object > Create Other > Plane from the Unity menu
  3. Attach a material to your plane, and a texture to the material.
  4. Check the texture importer settings on the texture and make sure Read/Write enable is set.
  5. Attach the DataField component to your Plane object.
  6. Find an object in your scene, or create an empty one, that will move across the plane. Attach it to the ExampleCharacterObject property of the DataField component.
  7. Run your game, and watch the ColorUnderCharacter property of the DataField as the object moves across it. It should match the texture color under the character.

Note that another component in your scene can now have a property of type DataField, and then call the GetColorData method on it directly -- the ExampleCharacterObject lookup in the Update method is just for demonstration purposes.

using UnityEngine;

public class DataField : MonoBehaviour
{
    // Attach something to this in the inspector and move it around
    // to demonstrate the data field lookup in action.
    public Transform ExampleCharacterObject = null;

    // Don't modify this, it will be updated as the character object moves around.
    public Color ColorUnderCharacter = Color.black;

    private Texture2D mTexture = null;

    // Check for required components on startup, and find the texture to be
    // used for the data field.
    void Start ()
    {
        if (collider == null)
        {
            Debug.LogError("collider required for DataField to perform texture lookups", gameObject);
        }
        if ((renderer == null) || (renderer.material == null) || (renderer.material.mainTexture == null))
        {
            Debug.LogError("renderer with a material and a main texture required for DataField to perform texture lookups", gameObject);
        }

        mTexture = renderer.material.mainTexture as Texture2D;
        if (mTexture == null)
        {
            Debug.LogError("Texture2D required for DataField to perform texture lookups", gameObject);
        }

        // Note that you must turn on Read/Write enable in the import settings for the
        // texture or else GetPixel will fail.
    }

    // Update demonstrates the use of GetColorData, but you can call
    // it from elsewhere too (this behaviour doesn't need an Update
    // method, this is just for illustration).
    void Update ()
    {
        if (ExampleCharacterObject != null)
        {
            ColorUnderCharacter = GetColorData(ExampleCharacterObject.position);
        }
    }

    // Find the color data under a given position.
    public Color GetColorData(Vector3 position)
    {
        // Default to black if we find no data.
        Color colorData = Color.black;

        // Create a down-pointing ray at the position.
        Ray ray = new Ray(position, Vector3.down);
        RaycastHit hit;

        // Check for a hit, using some arbitrarily long ray length.
        if (collider.Raycast(ray, out hit, 10000.0f))
        {
            colorData = mTexture.GetPixelBilinear(hit.textureCoord.x, hit.textureCoord.y);
        }

        return colorData;
    }
}

Hello Gary and “yoyo”.

Thanks for this good help.
However I have a bug I cannot solve : Only one half of the texture is read, the rest of it stays at value (0,0,0,0).
Have you any idea why this happens ?
Thanks a lot.

See example
http://www.rolandcahen.eu/UNITY/DataFieldReaderTest.unitypackage

RoCa

I found the solution.
Only half of the texture was calcuated
Not for negative x, z positions because the reader was at y = 0.
for some reasons the raycast could not lookup.
Once the texture reader is a 1m everything is fine