- Home /
Find world position of a Texture2D
How to find the position of a texture2D in world space?
My case is .. a texture's colour alpha is compared to an objects A texture colour alpha when this object is hit. I want to position of where this happened (from pixels)
What I have is a RaycastHit.textCoord of object A which I think may be used to find the position. But the texture coordination is then changed a bit, this is why I cannot use the hitpoint mentioned earlier.
What I need is somewhat a way to change pixels back to RaycastHit.textCoord which in turn it is changed to a position. But I guess this way is not possible, I don't know.
Below is a piece of code relevant
function CheckAlpha (texture:Texture2D, pixelUV:Vector2, directionCheck:boolean, other:Collider):boolean {
//pixelUV is the RaycastHit.textCoord
var uvX:int = pixelUV.x * texture.width;
var uvY:int = pixelUV.y * texture.height;
if (directionCheck) {
//This loop will pick up the pixel (a new uvY) from the
texture which is not transparent, starting from the bottom
till the uvY. If a pixel is picked up, another texture is
placed around the uvX and the new uvY. How can I get world
coordinates of this other texture?
for(var i = texture.height; i >= uvY; i--){
if(texture.GetPixel(uvX, i).a != 0.0)
return ApplyTexture(texture, uvX, i, shipDamageTexture, shipHoleTextureArray);
}
}
You can't find the position of a Texture2D in world space, because it's a data type and doesn't exist in world space.
You need to be more specific. Are you trying to find the position of a GUITexture? Textured plane? What?
Not true, you can find world position for every point on screen.
@Lovrenc A Texture2D is not on screen though unless it is used on a GUITexture, a textured object, etc. No more so than a float or boolean value.
I assume he is using it somewhere. Otherwise it would be pointless to search for. However, i have been surprised here before and should know better than to assume things.
@xikky provide more info.
the texture2D is neither a GUItexture nor a Plane. It is created when on a hitpoint between two objects.
To be more specific .. a the a texture's colour alpha is compared to an objects A texter colour alpha when this object is hit. I want to position of where this happened (from pixels)
What I have is a RaycastHit.textCoord of object A which I think may be used to find the position. But the texture coordination is then changed a bit, this is why I cannot use the hitpoint mentioned earlier.
What I need is somewhat a way to change pixels back to RaycastHit.textCoord which in turn it is changed to a position. But I guess this way is not possible, I don't know.
Answer by aldonaletto · Dec 31, 2012 at 06:04 PM
EDITED ANSWER: FIND 3D POSITION OF A GIVEN UV POINT
Ok, so you want to find the 3D position of a given texture element in the mesh. You're lucky: I answered recently a question about triangle interpolation, and found all the geometry needed in your case.
The basic idea is: given the UV coordinates of some texture point, iterate through the triangles until you find the one that contains it, then interpolate the triangle vertices to find the 3D point in the mesh; since mesh coordinates are in local space, you must also convert it to world space with transform.TransformPoint.
In order to check whether the point lies inside a given triangle, you can calculate the *barycentric coordinates* of the UV point: if any of them is negative, the point is outside the triangle. If the point is inside the triangle, the interpolation is incredibly easy to do because all it needs is the barycentric coordinates we've just calculated: just sum the triangle vertices multiplied by the respective barycentric coordinates, then apply TransformPoint to convert the resulting position to world space.
The function UvTo3D below does this: it must be attached to the mesh, and you must pass to it the UV coordinates of the desired point. The function gets the triangles, vertices and uvs of the mesh and looks for the point in the UV map. If the point lies inside one or more triangles, the 3D position in the first triangle found is returned; if not, the function returns Vector3.zero:
function UvTo3D(uv: Vector2): Vector3 {
var mesh: Mesh = GetComponent(MeshFilter).mesh;
var tris: int[] = mesh.triangles;
var uvs: Vector2[] = mesh.uv;
var verts: Vector3[] = mesh.vertices;
for (var i: int = 0; i < tris.length; i += 3){
var u1: Vector2 = uvs[tris[i]]; // get the triangle UVs
var u2: Vector2 = uvs[tris[i+1]];
var u3: Vector2 = uvs[tris[i+2]];
// calculate triangle area - if zero, skip it
var a: float = Area(u1, u2, u3); if (a == 0) continue;
// calculate barycentric coordinates of u1, u2 and u3
// if anyone is negative, point is outside the triangle: skip it
var a1: float = Area(u2, u3, uv)/a; if (a1 < 0) continue;
var a2: float = Area(u3, u1, uv)/a; if (a2 < 0) continue;
var a3: float = Area(u1, u2, uv)/a; if (a3 < 0) continue;
// point inside the triangle - find mesh position by interpolation...
var p3D: Vector3 = a1*verts[tris[i]]+a2*verts[tris[i+1]]+a3*verts[tris[i+2]];
// and return it in world coordinates:
return transform.TransformPoint(p3D);
}
// point outside any uv triangle: return Vector3.zero
return Vector3.zero;
}
// calculate signed triangle area using a kind of "2D cross product":
function Area(p1: Vector2, p2: Vector2, p3: Vector2): float {
var v1: Vector2 = p1 - p3;
var v2: Vector2 = p2 - p3;
return (v1.x * v2.y - v1.y * v2.x)/2;
}
If you want to know more details about triangle interpolation, take a look at the question mentioned above.
ORIGINAL ANSWER: COLOR PICKER, NOT WHAT THE OP ACTUALLY WANTED
If you're looking for a kind of "color picker" that gets the original texture color of the point under the cursor, this can be done - but with several restrictions:
1- The clicked object must have a mesh collider to return a valid UV pair in the RaycastHit structure;
2- The texture must have Read/Write enabled in the Import Settings (click the texture in the Project view to show its Import Settings, then select Texture Type = Advanced and mark Read/Write enabled);
3- A single pixel in the screen may correspond to lots of texture elements, thus the UV pair returned by RaycastHit may not be precise enough to detect small areas in the object.
The script below gets the texture color of the object point under the mouse, and you can compare it to whatever you want. It's based on the RaycastHit.textureCoord example, and only works for mesh colliders and textures marked as read/write. Attach it to the camera:
function Update () {
var ray = camera.ScreenPointToRay(Input.mousePosition);
var hit: RaycastHit;
if (Physics.Raycast(ray, hit)){
var meshCollider = hit.collider as MeshCollider;
if (!meshCollider) return; // only works for mesh colliders
var renderer: Renderer = hit.collider.renderer;
if (renderer && renderer.material && renderer.material.mainTexture){
var tex : Texture2D = renderer.material.mainTexture;
var pixelUV = hit.textureCoord;
pixelUV.x *= tex.width;
pixelUV.y *= tex.height;
var color: Color = tex.GetPixel(pixelUV.x, pixelUV.y);
// now you've got the color of the original texture
// at the hit point - compare it to what you want
}
}
}
Hi Aldo Naletto, thank you for your reply. The problem is that I am not using mouse inputs though.
Ok, so you want to do the opposite: given a texture element, find its world position in the surface of the object "painted" with this texture.
Supposing that your model doesn't use the same texture portion to cover more than one part of the model (what would make the job impossible), finding a given texture position in the model is a pain in the ass: you should convert the texture position to a UV pair (just divide by width and height), then iterate through the mesh triangles, getting each triangle in UV coordinates until the one that contained the UV point was found, and finally interpolate this triangle vertices to find the point in model space (you should use transform.TransformPoint to convert it to world space, if needed). If you want to find a single point, this may be tolerable - but forget about doing this for each element in the texture!
Please explain for which purpose you're trying to do such weird thing - probably there's another much easier way to reach the same result without doing all this work!
That's a pretty neat idea. In my case I have a map I'm pulling in from Google $$anonymous$$aps and using it as a texture on a plane. I'd like to place 3D objects on top of the location markers--I can get pixel coordinates of the markers in the texture. So I think this method will work. Once I work out the math.
I edited my answer and included a function that does what you want. Things became much easier than I supposed because the main search is done in 2D space, and also due to the fact that the "point inside triangle" test generates all we need to interpolate the point.
Hey thanks! I'll give it a try. Once I get it working, maybe I'll put it up as a gist for people to download.
Answer by GXMark · Jan 16 at 01:02 PM
Converted to C#
/// <summary>
/// Convert UV coordinate to World Coordinate
/// </summary>
/// <param name="uv">UV Coordinate</param>
/// <param name="mesh">Mesh</param>
/// <param name="transform">Transform of gameobject where mesh resides</param>
/// <returns>World Coordinate</returns>
public Vector3 UvTo3D(Vector2 uv, Mesh mesh, Transform transform)
{
int[] tris = mesh.triangles;
Vector2[] uvs = mesh.uv;
Vector3[] verts = mesh.vertices;
for (int i = 0; i < tris.Length; i += 3)
{
Vector2 u1 = uvs[tris[i]]; // get the triangle UVs
Vector2 u2 = uvs[tris[i + 1]];
Vector2 u3 = uvs[tris[i + 2]];
// Calculate triangle area - if zero, skip it
float a = Area(u1, u2, u3);
if (a == 0)
continue;
// Calculate barycentric coordinates of u1, u2 and u3
// If anyone is negative, point is outside the triangle: skip it
float a1 = Area(u2, u3, uv)/a;
if (a1 < 0)
continue;
float a2 = Area(u3, u1, uv)/a;
if (a2 < 0)
continue;
float a3 = Area(u1, u2, uv)/a;
if (a3 < 0)
continue;
// Point inside the triangle - find mesh position by interpolation...
Vector3 p3D = a1* verts[tris[i]] + a2 * verts[tris[i + 1]] + a3 * verts[tris[i + 2]];
// return it in world coordinates:
return transform.TransformPoint(p3D);
}
// Point outside any uv triangle
return Vector3.zero;
}
/// <summary>
/// Calculate signed triangle area using a kind of "2D cross product":
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
/// <returns>Area</returns>
private float Area(Vector2 p1, Vector2 p2, Vector2 p3)
{
Vector2 v1 = p1 - p3;
Vector2 v2 = p2 - p3;
return (v1.x * v2.y - v1.y * v2.x) / 2;
}