- Home /
Performance issue. Optimize or change SetPixel to allow for painting specific areas of a texture.
Hey, I'm having a performance issue where I am having moving game objects painting a texture as they move. Because I'm using texture splatting in shaderforge they are only painting in a single color (red) and the shader paints the actual texture.
The performance issue is that unity runs out of memory after (X) amount of seconds of 'painting' and it crashs. I've been looking into SetPixels & SetPixels32 which seem to be more efficient, but I can't figure out how to use them to paint specific pixel areas.
private void PaintAround(Texture2D tex, Vector2 pixelLocation, Color color, Vector2 mySize)
{
float widthRadius = ((mySize.x / 2) / objWidth) * tex.width;
float heightRadius = ((mySize.y / 2) / objHeight) * tex.height;
////Sets each individual pixel around the object to be a color
for (int x = (int)pixelLocation.x - (int)widthRadius; x < (int)pixelLocation.x + (int)widthRadius; x++)
{
for (int y = (int)pixelLocation.y - (int)heightRadius; y < (int)pixelLocation.y + (int)heightRadius; y++)
{
tex.SetPixel(-y, x, color);
}
}
tex.Apply();
}
Anyone got any ideas?
I figured out a solution to the crashing (it was something else, but still related). I am still looking to improve performance with SetPixels/SetPixels32 though.
collect the pixels you want to write and write them with SetPixels (plural) in one go.
Do I add each pixel into a Vector 2 array and then SetPixels to that array? Could you give an example as I haven't been able to get it to work yet.
Answer by Bunny83 · Jun 19, 2018 at 12:53 AM
The straight forward solution is simply this:
private void PaintAround(Texture2D tex, Vector2 pixelLocation, Color32 color, Vector2 mySize)
{
float widthRadius = ((mySize.x / 2) / objWidth) * tex.width;
float heightRadius = ((mySize.y / 2) / objHeight) * tex.height;
Color32[] colors = tex.GetPixels32();
int startX = Mathf.Clamp((int)pixelLocation.x - (int)widthRadius, 0, tex.width-1);
int endX = Mathf.Clamp((int)pixelLocation.x + (int)widthRadius, 0, tex.width-1);
int startY = Mathf.Clamp((int)pixelLocation.y - (int)heightRadius, 0, tex.height-1);
int endY = Mathf.Clamp((int)pixelLocation.y + (int)heightRadius, 0, tex.height-1);
for (int y = startY; y < endY y++)
{
int yOff = y * tex.width;
for (int x = startX; x < endX x++)
{
colors[yOff + x] = color;
}
}
tex.SetPixels32(colors);
tex.Apply();
}
Note that this solutions actually expects the pixel position to be in texture space. A texture's origin is the lower left corner. Your original line of code:
tex.SetPixel(-y, x, color);
Doesn't make much sense as you flipped x and y but you calculated them with the width and height of the texture. The proper clamping of each position is important as the colors array is a linear flattened array. If you would go beyond the right most pixel you would continue from the left side on the next line. Just as an example a 4x4 texture looks like this:
12 13 14 15
8 9 10 11
4 5 6 7
0 1 2 3
Though for performance reasons you may want to avoid calling GetPixels32 all the time. Since the texture doesn't change in between your calls you could just get the color array once in Start and just store it in a class member variable. Just change the values you want to change in the array and call SetPixels32 afterwards.
We can't get more into detail since we don't know how you actually use the texture or where and how you determine your "pixelLocation"
Your answer
Follow this Question
Related Questions
Unity Best Practices - Cached Components 1 Answer
Use SetPixels, but dont set unvisible Pixels 1 Answer
SetPixels32 called with invalid number if pixels in the array 1 Answer
Optimize mesh for runtime performance 0 Answers
Punching holes through a texture and making them regenerate back again in an efficient way 0 Answers