Painting a texture onto another texture

Or Particle collision point to UV 2: SetPixels Boogaloo

So I have my system setup where I get the UV coordinates of where particles collide, but now I’m stuck with a different problem. I want to draw a texture over a heightmap texture so that I can get the blood on the floor/walls/ceiling/grandma to stick. So far I can do this with circles using some borrowed code, but I want to draw a texture on impact instead.

Here’s the code I’m using that draws circles. Unfortunately, it isn’t super readable to me as it suffers from Single Letter Named Variables:

    public void Circle (Texture2D tex, int cx, int cy, int r, Color col)
    {
        int x, y, px, nx, py, ny, d;
        Color[] tempArray = tex.GetPixels ();
        int length = tempArray.Length;
        for (x = 0; x <= r; x++) {
            d = (int)Mathf.Ceil (Mathf.Sqrt (r * r - x * x));
            for (y = 0; y <= d; y++) {
                px = cx + x;
                nx = cx - x;
                py = cy + y;
                ny = cy - y;

                if (py * tex.width + px >= 0 && py * tex.width + px < length) {
                    tempArray [py * tex.width + px] = col;
                }
                if (py * tex.width + nx >= 0 && py * tex.width + nx < length) {
                    tempArray [py * tex.width + nx] = col; 
                }
                if (ny * tex.width + px >= 0 && ny * tex.width + px < length) {
                    tempArray [ny * tex.width + px] = col; 
                }
                if (ny * tex.width + nx >= 0 && ny * tex.width + nx < length) {
                    tempArray [ny * tex.width + nx] = col; 
                }
            }
        }   
        tex.SetPixels(tempArray);
        tex.Apply ();
    }

I get it kinda but the math there doesn’t provide me with anything I can take away from to do what I want to do. I have the very bare minimum start of my own function here:

    public void DrawSplatter (Texture2D heightMap, Texture2D splatterTexture, int texelX, int texelY, Color colour) {
        Color[] heightmapArray = heightMap.GetPixels ();
        Color[] splatterArray = splatterTexture.GetPixels ();
    }

As you can see, I’m not very far in. What I think I need to do is the following:

  • Find where the intersection point is and figure out if there’s any points on the splatterTexture that fall out of bounds so I can ignore that part and not paint it. I’m not sure how I’m supposed to do this because I’m not entirely sure how to find the valid intersections.
  • I then need to actually draw over the heightmap texture. I’m not entirely sure how to do this either because I have no idea how to handle alpha values in this sort of situation. I suppose the best option would be to use Mathf.Lerp to blend them and use the alpha value for T?
  • I also want to be able to use variable sizes for the splatterTexture so it doesn’t look so uniform. Is there a way I can scale the texture in the script so that I can accomplish this?

I know I’m asking kind of a lot here, but even being pointed in the right direction, especially for 1 and 3, would be great, thanks.

So I was able to get a huge performance increase by switching from SetPixels to SetPixels32, but aside from that, I’m still a bit lost here. Bump for the morning/afternoon crew I guess.

It’s all a matter of for loops and carefully converting between source and dest coordinates.

It’s easiest if you can assume the texture you’re drawing is always bigger than where you’re drawing it, that is, you’re going to be scaling it down. So the big picture is: iterate over your destination pixels (i.e., the texture you’re drawing into), look up the color from your source texture (the one you’re drawing), and stuff it in.

Start by finding the range of destination pixels you’re going to iterate over. In X, for example, you start at basically centerX - halfSize, but you want to watch out for not going out of bounds, so it’s really Max(centerX - halfSize, 0). Similarly, you’ll iterate up to Min(centerX + halfSize, destTexture.width). Same for Y.

And then to find the source pixel, you figure out how far you are across (and down) the area you’re drawing, and then just go that same fraction of the way across (and down) your splatter texture. Look up the color from there, stuff it into your destination texture, and repeat until done.

It’s… a bit more complicated than just stuffing it in. I need to account for the alpha values of the source texture and I’m not sure how to do that entirely. So far the best idea I have is to use a lerp and remap the 0 - 255 of the alpha value to 0 - 1 so I can use it for T.

Right, but Texture2D is a one-dimensional array, which makes this a bit more challenging. I’m not sure how to handle the bounds rejection at all. I know I have to do it, but I have no idea how.

You’re getting ahead of yourself. Because you find this tricky, you should take it in stages. Even ignoring alpha may be a bit much. I’d approach it like this:

  • Draw a texture at 1:1 scale, without worrying about going off the edges (I’ll always test in the middle!).
  • Make it so it properly clips at the edges.
  • Add support for scaling as we draw (i.e., the destination area we’re drawing in may be different from the source texture size).
  • Add support for alpha.

If you’re still struggling with steps 1-3, worrying about step 4 is a little premature.

When you do get to step 4, yeah, you’ll do some math involving the alpha of the source texture and the destination color. Basically it’s just a mixing formula which I bet you can find in wikipedia. But get yourself some success first!

That’s step 1. Again, math to the rescue: your index into this array is y * width + x. (See if you can understand why, maybe by working through some toy examples on paper, imagining a 4x8 image.)

That’s step 2. After you’ve done step 1, this step will seem a lot more clear.

So. Go write some code!