Search code examples
c#unity-game-enginetexturestexture2d

Set all pixels to be red (255,0,0) using Texture2D.SetPixelData, but the pixels change to a multicolour assortment


I have the following code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TextureScript : MonoBehaviour
{

    Texture2D texture;

    // Start is called before the first frame update
    void Start()
    {
        texture = new Texture2D(256,256, TextureFormat.RGB24, true);

        var rectTransform = transform.GetComponent<RectTransform>();
        rectTransform.sizeDelta = new Vector2(texture.width, texture.height);

        int pixelCount = texture.width * texture.height;

        Queue<Color> queue = new Queue<Color>();

        for(int i = 0; i < pixelCount; i++)
        {
            queue.Enqueue(new Color(255,0,0));
        }

        Color[] colorArray = queue.ToArray();

        texture.SetPixelData(colorArray, 0, 0);
        texture.filterMode = FilterMode.Point;
        texture.Apply(updateMipmaps: false);

        GetComponent<RawImage>().material.mainTexture = texture;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

Which produces this:

The image produced by aforementioned code.

I am unsure as to what could be producing it. Previously I was using a byte array for tests which worked, but my work now requires a queue, and to then convert this queue into an array (which I have done using .ToArray()).

I also used RGBA32 rather than RGB24 with varying alpha values of 255, 128, 1, & 0; however, this produced a nearly see-through image each time.

Thank-you for your help.


Solution

    • Why use a Queue instead of simply an array?
    • Why use SetPixelData instead of simply SetPixels?

    I would simply do e.g.

    //var pixels = texture.GetPixels();
    // or even simply
    var pixels = new Color[texture.width * texture.height];
    for(var i = 0; i < pixels.Length; i++)
    {
        pixels[i] = color.Red;
    }
    texture.SetPixels(pixels);
    texture.Apply();
    

    SetPixelData is rather

    useful if you want to load compressed or other non-color texture format data into a texture.

    What happens in your case is: Color has a byte size of 4 float (=16 byte) since it also has an alpha value! Your texture is using RGB24 and therefore only expecting 3 bytes (=24 bit) per pixel.

    So in case if you really want to stick to that (might be faster - or not ^^) you would rather have to do e.g.

    var pixels = new byte[texture.width * texture.height * 3];
    for(var i = 0; i < pixels.Length; i+=3)
    {
        pixels[i] = 255; // R
        pixels[i+1] = 0; // G
        pixels[i+2] = 0; // B
    }
    texture.SetPixelData(pixels, 0, 0);
    texture.Apply();
    

    also regarding

    new Color(255,0,0)
    

    note that Color takes float arguments from 0 to 1. If you are looking for a byte based input rather go for Color32 and SetPixels32


    Further note that RawImage is actually quite expensive - you could stick to an Image component and simply create a Sprite from your Texture2D

    GetComponent<Image>().sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 0.5f);