- Home /
SetPixels and ExportToPNG - Alpha value missing?,
Hello. I made a script to generate a random map and then export it into PNG format. Everything works fine except for one sprite with alpha values that apparently get lost somewhere.
(Left is scene view, right is the exported PNG). Code I'm working with:
void ExportToPNG()
{
int ppu = 16;
//size x ppu 100x16
Texture2D tex = new Texture2D(width*ppu, height*ppu, TextureFormat.RGBA32, false);
[removed bits of the big loop code here unrelated to the issue]
//drawing boss icon on the PNG file
tex.SetPixels((x*ppu)-8, (y*ppu)-8, ppu*2, ppu*2, GetCurrentSprite(_tilemaps[tm].GetSprite(new Vector3Int(x,y,0))).GetPixels());
tex.Apply();
byte[] bytes = tex.EncodeToPNG();
File.WriteAllBytes(Application.dataPath + "/../map_"+mapsGenerated+".png", bytes);
mapsGenerated++;
//clearMap();
}
Texture2D GetCurrentSprite(Sprite sprite)
{
Color[] pixels = sprite.texture.GetPixels((int)sprite.textureRect.x, (int)sprite.textureRect.y, (int)sprite.textureRect.width, (int)sprite.textureRect.height);
Texture2D text = new Texture2D((int)sprite.textureRect.width, (int)sprite.textureRect.height, TextureFormat.RGBA32, false);
text.SetPixels(pixels);
text.Apply();
return text;
}
I've been stuck here for a while now and can't seem to find a solution or what's actually going wrong. Any ideas? Thanks in advance.
,
For clarification, at what point is the alpha channel lost? For example, is it when the exported/written PNG file is reimported into Unity?
Might be better to add some intermediate steps instead of doing everything in one go. One thing i would do is to actually log the Color[] pixels to see if the alpha channel of the pixels is correct. You could also use GetPixel instead of GetPixels in a loop, so you can check every pixel yourself. That way you can check if the pixel you are reading is an alpha pixel and set the value you need accordingly.
Answer by Bunny83 · Dec 16, 2021 at 02:36 PM
I think you have the wrong idea about what you're doing here. You have this line of code and comment:
//drawing boss icon on the PNG file
tex.SetPixels((x*ppu)-8, (y*ppu)-8, ppu*2, ppu*2, GetCurrentSprite(_tilemaps[tm].GetSprite(new Vector3Int(x,y,0))).GetPixels());
This does not "draw" your texture onto the other texture. This replaces the pixels in the rectangular region in target image with the pixels of your sprite texture. It does not "draw" the image onto the background. This sounds like you want to do the same that was asked over here.
Ah! it seems in my $$anonymous$$d I expected it to draw 'invisible' pixels as long as they had an alpha value of 0. I've now fixed it by doing a single SetPixel at a time and skipping the Alpha = 0 pixels. Thanks!
While you can use several SetPixel calls, it's quite inefficient. It probably doesn't matter for your usecase. However combining the pixels manually inside a Color array is generally way faster than calling SetPixel in a loop.
Answer by blaize26 · Dec 16, 2021 at 12:21 PM
@Eno-Khaon @metalted It seems to go missing during the "GetCurrentSprite" call. I've tried debugging it as such:
Texture2D GetCurrentSprite(Sprite sprite)
{
Color[] pixels = sprite.texture.GetPixels((int)sprite.textureRect.x, (int)sprite.textureRect.y, (int)sprite.textureRect.width, (int)sprite.textureRect.height);
//the troubled icon is the only 32x32 sprite
if(sprite.textureRect.width==32)
{
for(int p=0;p<pixels.Length/2;p++)
{
pixels[p] = new Color(0f, 0f, 0f, 0f);
}
}
Texture2D text = new Texture2D((int)sprite.textureRect.width, (int)sprite.textureRect.height, TextureFormat.RGBA32, false);
text.SetPixels(pixels);
text.Apply();
return text;
}
result:
new Color(1f, 1f, 1f, 0) returns black as well, while new Color(1f, 1f, 1f, 1f) returns white (as it should).
Basicly ignoring the alpha no matter what on the exported PNG.
Hmm thats weird, so basically you cant make an alpha channel even if you set it directly. Don't really understand why thats happening. Maybe you can try using another Color struct like Color32? Think that supports alpha as well, right?
Exact same behavior with Color32 as well. I'm really stumped by this. I've also tried Color.clear, still get black and no transparency.
What instead of tex.SetPixels((x*ppu)-8, (y*ppu)-8, ppu*2, ppu*2, GetCurrentSprite(_tilemaps[tm].GetSprite(new Vector3Int(x,y,0))).GetPixels());
you try something like:
Color[] bossPixels = GetCurrentSprite(_tilemaps[tm].GetSprite(new Vector3Int(x, y, 0))).GetPixels();
int pixelIndex = 0;
for(int i = x * ppu - 8, i < x * ppu + 8; i++)
{
for (int j = y * ppu - 8, j < y * ppu + 8; j++)
{
Color pixelColor = bossPixels[pixelIndex];
if(pixelColor.a != 0f)
{
tex.SetPixel(i, j, pixelColor);
}
pixelIndex++;
}
}
As in, if the pixel in the sprite is alpha, don't set the pixel at all in the result. Not sure if this code works right, cause its kinda hard to test it myself and im not sure about the pixel order, so you might end up with a weird sprite. But at least you can test if it helps with the transparancy.