Search Unity

How to change the sprite of an object pixel by pixel

Discussion in 'Scripting' started by Tovey-Ansell, Oct 23, 2016.

  1. Tovey-Ansell

    Tovey-Ansell

    Joined:
    Jul 8, 2015
    Posts:
    150
    I'm pretty new to this, and anything relating to textures/sprites/particles/whatever I really haven't wrapped my head around yet, so bear with me.

    I'm trying to create a quad that displays a white noise visual effect, by going through each pixel and randomly setting them to either black or white each new frame, but I really don't know how to go about doing this, how do I change the pixels of the sprite/texture (what's the difference?) and then apply that as a new sprite/texture?

    Here's the code I'm using, which is executing but not having any affect.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class WhiteNoiseController : MonoBehaviour
    5. {
    6.     void Start()
    7.     {
    8.         Texture2D texture = new Texture2D(128, 128);
    9.         GetComponent<Renderer>().material.mainTexture = texture;
    10.  
    11.         for (int y = 0; y < texture.height; y++)
    12.         {
    13.             for (int x = 0; x < texture.width; x++) //Goes through each pixel
    14.             {
    15.                 Color pixelColour;
    16.                 if (Random.Range(0, 2) == 1 ) //50/50 chance it will be black or white
    17.                 {
    18.                     pixelColour = new Color(0, 0, 0, 1);
    19.                 }
    20.                 else
    21.                 {
    22.                     pixelColour = new Color(1, 1, 1, 1);
    23.                 }
    24.                 texture.SetPixel(x, y, pixelColour);
    25.             }
    26.         }
    27.         Debug.Log("Texture applied");
    28.         texture.Apply();
    29.     }
    30. }
    Any help would be very much appreciated :)

    Thanks,
    Fred T.A
     
  2. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Sprites are objects in unity to encapsulate drawing 2D images. And one of the properties of a Sprite is its texture. it is this texture that is what is used

    Now you can't directly set a texture of an existing sprite, its read only. You can create your own texture, and then create a sprite from that, and set that sprite to the sprite of the Sprite Renderer like this:
    Code (CSharp):
    1.     void Start()
    2.     {
    3.         Texture2D texture = new Texture2D(128, 128);
    4.         Sprite sprite = Sprite.Create(texture, new Rect(0, 0, 128, 128), Vector2.zero);
    5.         GetComponent<SpriteRenderer>().sprite = sprite;
    6.      
    7.         for (int y = 0; y < texture.height; y++)
    8.         {
    9.             for (int x = 0; x < texture.width; x++) //Goes through each pixel
    10.             {
    11.                 Color pixelColour;
    12.                if (Random.Range(0, 2) == 1) //50/50 chance it will be black or white
    13.                 {
    14.                     pixelColour = new Color(0, 0, 0, 1);
    15.                 }
    16.                 else
    17.                 {
    18.                     pixelColour = new Color(1, 1, 1, 1);
    19.                 }
    20.                 texture.SetPixel(x, y, pixelColour);
    21.             }
    22.         }
    23.         texture.Apply();
    24.  
    Now the mainTexture of the material is used by the shader. The actual color Values of the material are not used in the default SpriteShaders, but the alpha value of the color of this material is used. The Tint Color of this material is also used to modify the color values of your Sprite. Other Shader's use the color value of the materia's mainTexture. In fact that is how 3D objects are colored.

    Edit: Just tested and the alpha values of the mainTexture are also not used by the Sprite Shaders.. its purely the color and alpha of the tint Value you set that is used. You could write your own Sprite Shader that used the material's mainTexture for information, but currently the SpriteShaders ignore the material's texture all together
     
    Last edited: Oct 23, 2016
    Tovey-Ansell likes this.
  3. Tovey-Ansell

    Tovey-Ansell

    Joined:
    Jul 8, 2015
    Posts:
    150
    Awesome, that works great!

    One last thing though, for the quad to cover the size of the area I need I must use "1024" for the values on the first two lines, not "128", obviously performing 1024^2 calculations each frame update isn't exactly feasible, is there a way to optimize this? say have a lower number of pixels for a texture of a certain size?
     
  4. Tovey-Ansell

    Tovey-Ansell

    Joined:
    Jul 8, 2015
    Posts:
    150
    Never mind, found a way around this by setting a block of 4 pixels to black or white, not each individual pixel..

    Code (CSharp):
    1.     void Update()
    2.     {
    3.         Texture2D texture = new Texture2D(413, 715);
    4.         Sprite sprite = Sprite.Create(texture, new Rect(0, 0, 413, 715), Vector2.zero);
    5.         GetComponent<SpriteRenderer>().sprite = sprite;
    6.  
    7.         for (int y = 0; y < texture.height; y = y + 2)
    8.         {
    9.             for (int x = 0; x < texture.width; x = x + 2) //Goes through each 2 x 2 block of pixels
    10.             {
    11.                  Color pixelColour;
    12.                 if (Random.Range(0, 2) == 1) //50/50 chance it will be black or white
    13.                 {
    14.                     pixelColour = new Color(0, 0, 0, 1); //Black
    15.                 }
    16.                 else
    17.                 {
    18.                     pixelColour = new Color(1, 1, 1, 1); //White
    19.                 }
    20.                 texture.SetPixel(x, y, pixelColour);
    21.                 texture.SetPixel(x + 1, y, pixelColour);
    22.                 texture.SetPixel(x, y + 1, pixelColour);
    23.                 texture.SetPixel(x + 1, y + 1, pixelColour);
    24.                 //Setting each pixel is too CPU hungry - so set them in blocks of 4
    25.                 }
    26.             }
    27.          texture.Apply();
    28.     }