- Home /
Checking the transparency of a mesh with a piece of a texture
My program is taking in a texture and splitting the texture into randomly sized pieces that are made into 2D meshes (like a puzzle). The textures I use often contain large sections of transparency so I often end up with some puzzle pieces (meshes) where all the pixels are transparent. How do I identify if the texture mapped to the meshes UV coordinates are transparent?
I've read here: Get average color of the texture assigned to a mesh triangle
But I don't understand this part in the answer: "You'd code a likewise-not-too-difficult function to examine each pixel within the triangle based on its 3 UV coordinates". I think I just need a nudge in the right direction on how to get the pixels of a mesh under a triangle.
Here is my code:
private void CreatePuzzlePieces(List<VoronoiCell2> cells, List<Vector2> imageBox)
{
for (int i = 0; i < cells.Count; i++)
{
VoronoiCell2 cell = cells[i];
List<Vector2> vertices = new List<Vector2>();
// Get the vertices of the mesh (puzzle piece)
for (int j = 0; j < cell.edges.Count; j++)
{
Vector3 p3 = cell.edges[j].p1.ToVector3();
Vector3 p2 = cell.edges[j].p2.ToVector3();
vertices.Add(p2);
vertices.Add(p3);
}
Vector3[] meshVertices = new Vector3[vertices.Count];
Vector2[] uv = new Vector2[vertices.Count];
int triangleCount = (vertices.Count - 2) * 3;
int[] triangles = new int[triangleCount];
// Scale the mesh to fit within the imagebox
for (int j = 0; j < vertices.Count; j++)
{
meshVertices[j] = new Vector3((vertices[j].x - imageBox[0].x) / (imageBox[1].x - imageBox[0].x),
(vertices[j].y - imageBox[0].y) / (imageBox[1].y - imageBox[0].y));
uv[j] = meshVertices[j];
}
// Build triangles
// This will only work for polygons without any concave features and it
// uses a more efficent approach (fewer triangles) than DisplayVoronoiCells()
// but may not work for polygons with 9 or more sides but that is unlikely to
// occur with Voronoi algorithm
int triPos = 0;
int curr = 0;
int loop = 1;
for (int j = 0; j < triangleCount; j++)
{
if (triPos == 3)
{
curr -= 1 * loop;
triangles[j] = curr;
triPos = 0;
}
else
{
if (curr >= vertices.Count)
{
curr = 0;
loop++;
}
triangles[j] = curr;
}
curr += 1 * loop;
triPos++;
}
Mesh mesh = new Mesh();
mesh.vertices = meshVertices;
mesh.uv = uv;
mesh.triangles = triangles;
GameObject goPiece = new GameObject("PuzzlePiece", typeof(MeshFilter), typeof(MeshRenderer));
goPiece.transform.position = puzzleBox.transform.position;
MeshRenderer rendererPiece = goPiece.GetComponent<MeshRenderer>();
goPiece.GetComponent<MeshFilter>().mesh = mesh;
rendererPiece.material = image;
// ------------------------------------------------------------------------
// Here is where I want to check the percent of mesh (pixels) that is fully transparent:
if ( mesh contains > 99% transparent pixels) do something
// ------------------------------------------------------------------------
}
}
Answer by nmill99 · Jun 08, 2020 at 12:41 PM
I've managed to make a solution to my particular problem. The way it works is like this:
For the particular puzzle piece (mesh), I call the function "PercentTransparent()".
For each triangle in the mesh, I get the UV coordinates (same as vertices for me) and translate those into pixel coordinates. For example, if the texture is 800x600, the uv coordinates (1.0f,1.0f) are translated to (800,600).
I also get the vertex with the largest y value. This is our starting point. And then I get the lowest y value, which will be our end point.
Then, starting from the largest y value position, I first go left looking to see if the pixel is in our triangle and once I'm not, I break and go right from the same starting point until again I'm no longer in the triangle.
The result is slow so it would not work on an Update() call but could work if pre-loaded which is my case.
I got this algorithm idea from paper A Parallel Algorithm for Polygon Rasterization by Juan Pineda 1988 and in particular this diagram:
public class PuzzlePiece
{
public Mesh Mesh { get; }
public Material Image { get; }
private float PercentTransparent()
{
float percent = 0f;
string output = "Checking transparency on piece " + Index + "...\n";
output += "Texture dimensions = " + Image.mainTexture.width + ", " + Image.mainTexture.height + "\n";
output += " - Number of triangles = " + Mesh.triangles.Length + "\n";
output += " - Number of vertices = " + Mesh.vertices.Length + "\n";
output += " - Number of uvs = " + Mesh.uv.Length + "\n";
// Iterate through each triangle
long totalPixelCount = 0;
long transparentPixelCount = 0;
var triangles = Mesh.triangles;
var uv = Mesh.uv;
for (int i = 0; i < triangles.Length; i += 3)
{
// For each triangle (3 vertices), establish new UV coordiantes based on pixel position
List<Vector2> uvPixelVertices = new List<Vector2>();
int startYPos = 0;
int startXPos = 0;
int endYPos = 0;
output += " Triangle" + i + ":\n";
for (int j=0; j<3; j++)
{
output += " Normalized UV " + j + ": " + uv[triangles[i + j]] + "\n";
Vector2 vertices = new Vector2(uv[triangles[i + j]].x * Image.mainTexture.width,
uv[triangles[i + j]].y * Image.mainTexture.height);
uvPixelVertices.Add(vertices);
if (Mathf.FloorToInt(vertices.y) > startYPos)
{
startYPos = Mathf.FloorToInt(vertices.y);
startXPos = Mathf.FloorToInt(vertices.x); ;
}
if (j == 0 || Mathf.FloorToInt(vertices.y) < endYPos)
{
endYPos = Mathf.FloorToInt(vertices.y);
}
output += " - Pixel UV: " + vertices + "\n";
}
// Starting from the top, we go down one row at a time
// and identify all the pixels inside our triangle
// if they are inside, we track the pixel transparency
// (Assuming : a<1.0 = transparent)
for (int y = startYPos; y >= endYPos; y--)
{
bool foundPixelOnLeft = false;
bool foundPixelOnRight = false;
int x = startXPos;
// First look left
while (true)
{
if (x < 0)
break;
if (PuzzleUtilities.IsPointInPolygon(new Vector2(x, y), uvPixelVertices))
{
if (!foundPixelOnLeft)
foundPixelOnLeft = true;
totalPixelCount++;
if ((Image.mainTexture as Texture2D).GetPixel(x, y).a < 1.0)
transparentPixelCount++;
}
else if (foundPixelOnLeft)
{
break;
}
x--;
}
// Now look right
x = startXPos + 1;
while(true)
{
if (x > Image.mainTexture.width)
break;
if (PuzzleUtilities.IsPointInPolygon(new Vector2(x, y), uvPixelVertices))
{
if (!foundPixelOnRight)
foundPixelOnRight = true;
totalPixelCount++;
if ((Image.mainTexture as Texture2D).GetPixel(x, y).a < 1.0)
transparentPixelCount++;
}
else if (foundPixelOnLeft)
{
break;
}
else if (foundPixelOnRight)
{
break;
}
x++;
}
}
}
output += "FINISHED! Total pixels = " + totalPixelCount + " Transparent Pixels = " + transparentPixelCount + "\n";
Debug.Log(output);
return (float) transparentPixelCount / totalPixelCount;
}
}
public static class PuzzleUtilities
{
public static bool IsPointInPolygon(Vector2 point, List<Vector2> polygon)
{
// Even-odd rule
// https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule
bool res = false;
int j = polygon.Count - 1;
for (int i = 0; i < polygon.Count; i++)
{
if (((polygon[i].y > point.y) != (polygon[j].y > point.y)) &&
(point.x < polygon[i].x + (polygon[j].x - polygon[i].x) * (point.y - polygon[i].y) / (polygon[j].y - polygon[i].y)))
{
res = !res;
}
j = i;
}
return res;
}
}
Your answer
Follow this Question
Related Questions
Changing the color and transparency of a 3D model at runtime 1 Answer
Change color of mesh triangle based on Y position in world space 1 Answer
Render mesh by specifying color of triangle 1 Answer
Get average color of the texture assigned to a mesh triangle 2 Answers
Vertex Color to Texture2D?? 0 Answers