Manually modify a texture (C#)

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By twoski

Let’s say i have a 100x100px sprite in a very simple scene.

I want to manually modify the sprite colors. For example i want to make all of its pixels green.

Right now, the sprite has an ImageTexture. I am procedurally assigning an Image object to the ImageTexture.

    Image i = new Image();
    i.Create(100, 100, false, Image.Format.Rgb8);

    ImageTexture t = this.Texture as ImageTexture;

    i.Lock();
    i.SetPixel(0, 0, new Color(1f, 1f, 1f)); 

    t.SetData(i);

This causes godot to throw an error:

Condition ' !texture->active ' is true.
 ImageTexture.cs:127 @ void Godot.ImageTexture.SetData(Godot.Image )

How do i go about doing this properly?

:bust_in_silhouette: Reply From: Zylann

There are a few issues with your code:

  • You are locking the image but never unlocking it
  • You are calling SetData() on an ImageTexture but it was never initialized. It’s not the right function to create it from an image (honestly I’m not sure what are the true behavior of SetData, the doc is missing some details).

Try this instead:

i.Lock();
i.SetPixel(0, 0, new Color(1f, 1f, 1f)); 
i.Unlock();

t.CreateFromImage(i, 0)

I had set the texture property of the sprite to an ImageTexture via the inspector but i guess that was the wrong approach.

I tried this and it works:

    Image i = new Image();
    i.Create(1920, 1080, false, Image.Format.Rgb8);
    i.Lock();

    ImageTexture t = new ImageTexture();

    Random r = new Random();

    for (int y = 0; y < i.GetHeight(); y++)
    {
        for (int x = 0; x < i.GetWidth(); x++)
        {
            int rand = r.Next(0, 100);
            float f = rand / 100f;

            i.SetPixel(x, y, new Color(f, f, f));
        }
    }

    i.Unlock();
    t.CreateFromImage(i);

    this.Texture = t;       

Though i’m not sure if this is the most efficient way to do it. I want to run this code as fast as possible so perhaps creating a new imagetexture and image every time it runs is a bad idea.

twoski | 2019-08-17 19:34

There are a few ways to go, depending on what you are doing. If you want to actually edit the image, either reupload it entirely like you are doing, or use VisualServer.texture_set_data_partial to upload only a modified sub-rectangle (but might not be worth it if your sprite is small like 100x100).

But if your use case is only about tinting the image, generating or replacing colors at runtime, a shader could do this effect much more efficiently.

Zylann | 2019-08-17 19:39