- Home /
Texture Scanner
Hello people, i want to make a machine that will consecutive scan a the terrain in front of it and if it is a certain texture it will walk, so it will always stay on the path. How can i do that?
This will be so, so simple when I can get my package on the Asset Store. Waiting for lawyers. $$anonymous$$eep an eye on http://qlcomp.com. I'll let you know when it's up.
Answer by save · Aug 13, 2013 at 09:28 AM
Use a Raycast downwards from the transform position if you want the hit texture use RaycastHit.transform.renderer.sharedMaterial.mainTexture. If you want the color data, read the mainTexture with GetPixel or GetPixelBilinear. If it's a certain texture, or color, then translate the object.
Example 1 - Extracting a texture, comparing with others
This is a basic example of getting a texture from a RaycastHit. If it exists in a certain array of textures, we execute something.
#pragma strict
var textures : Texture2D[];
static var currentTexture : Texture2D;
function Update () {
// Get the current texture at transform position
currentTexture = TextureRaycast(transform.position, Vector3.down);
// If the texture is within range, do something...
if (IsWithinTextureRange(currentTexture, textures)) {
transform.Translate(Vector3.forward*Time.deltaTime);
}
}
// Raycast to get current texture
function TextureRaycast (pos : Vector3, dir : Vector3) : Texture2D {
var texture : Texture2D;
var hit : RaycastHit;
if (Physics.Raycast(pos, dir, hit)) {
texture = hit.transform.renderer.sharedMaterial.mainTexture as Texture2D;
}
return texture;
}
// See if texture is within range of approved textures
function IsWithinTextureRange (thisTexture : Texture2D, approvedTextures : Texture2D[]) : boolean {
for (var x = 0; x<approvedTextures.Length; x++)
if (currentTexture==approvedTextures[x])
return true;
return false;
}
Example 2 - Extracting a color, comparing within a range
Here we extract a color from a pixel at position and if the pixel color is within the range of colorMin and colorMax, we execute something.
#pragma strict
var colorMin : Color = Color.black;
var colorMax : Color = Color.white;
static var currentColor : Color;
function Update () {
// Get the current color at transform position
currentColor = GetColorAtPosition(PixelRaycast(transform.position, Vector3.down));
// If the color is within range, do something...
if (IsWithinColorRange(currentColor, colorMin, colorMax)) {
transform.Translate(Vector3.forward*Time.deltaTime);
}
}
// Return the texture pixel color from a PixelInfo object
function GetColorAtPosition (pixelInfo : PixelInfo) : Color {
if (pixelInfo.texture!=null)
return pixelInfo.texture.GetPixel(pixelInfo.position.x, pixelInfo.position.y);
else
return Color.black;
}
// Raycast to create a PixelInfo object
function PixelRaycast (pos : Vector3, dir : Vector3) : PixelInfo {
var pixelInfo = new PixelInfo();
var hit : RaycastHit;
if (Physics.Raycast(pos, dir, hit)) {
pixelInfo.texture = hit.transform.renderer.sharedMaterial.mainTexture as Texture2D;
pixelInfo.position = hit.textureCoord;
}
return pixelInfo;
}
// See if color is within range (Example: Color.black == R0.0, G0.0, B0.0, Color.white == R1.0, G1.0, B1.0)
function IsWithinColorRange (thisColor : Color, minColor : Color, maxColor : Color) : boolean {
return (thisColor.r>=minColor.r&&thisColor.r<=maxColor.r&&
thisColor.g>=minColor.g&&thisColor.g<=maxColor.g&&
thisColor.b>=minColor.b&&thisColor.b<=maxColor.b);
}
// Helper class for returning pixels from a texture
class PixelInfo {
var texture : Texture2D;
var position : Vector2;
}
There are some important things to keep in mind:
The two methods only reads mesh colliders.
Textures that is supposed to be read need Read/Write enabled in their import settings.
The script doesn't handle alternative routes, it only translates an object forward if the color or texture is within range. The reason is that such behavior is much more complex, where you would begin to script an AI. For instance, I would create a direction array if the color is "bad" below, and look for a "good" color, with where the transform came from last (to finally turn back instead of bounce in a new direction at corners). A good place to continue that is within:
if (IsWithinColorRange(currentColor, colorMin, colorMax)) { // The color is good... } else { // The color is bad... }
GetPixelAtPosition in example 2 returns black if nothing is found at position, which could lead to unwanted behavior if colorMin is black.
The thing you're asking isn't perfectly easy as the implementation of this requires a lot of special case scenario handling. But hopefully the example script can be a good start to something amazing.
Update: I understand you're after comparing textures and not colors, hence the two different examples.
Yeah, the second example is the other method I was thinking about, but I thought less suited for his needs. On both of these examples though, you should use shared$$anonymous$$aterial ins$$anonymous$$d of material, and both only work if you have only one material on your mesh. If you have more than one, you need to use the hit triangle, to get to what material was on the hit spot.
Thank you both a lot guys! One question: the texture i use for path is not 100% solid. I mean it has a small transparency so it looks like its hidden from the leaves and the grass. Although its very easy to spot. Will it be a problem?
it doesn't really matter... you'll be detecting the hit object, and comparing either color or texture of his material, with what you know. It's not like you are analyzing the image from the camera. ^^
Hehe yeah i mean if there is transparency there will be shown a x% of the forest ground texture and a y% of the rocky path texture. I hope it wont matter :D
Answer by Joyrider · Aug 13, 2013 at 08:39 AM
My guess would be :
1/ using Physics.RayCast,
2/ get the triangle id that was hit data,
3/ check by which submesh that triangle is used,
4/ get the material from that submesh (same id as the submesh in the material array),
5/ and check the mainTexture
But this is only one possible solution, maybe someone else has other suggestions..