# Paint

I was tasked with making it possible for balls and elastics that were thrown or shot to paint objects that they collided with. I started out this task by researching various already existing paint systems and after that, I picked one and modified it to suit our project.

## Research

### BL1NDSHOT's paint system

I started out by looking for videos and blogs of people who already made a paint system in Unity. The first system I found on this blog: [https://sergeyshamov.wordpress.com/2018/05/22/paint-splat-on-objects-in-game/](https://sergeyshamov.wordpress.com/2018/05/22/paint-splat-on-objects-in-game/) . At first it looked quite promising, however I did not get the shader to work with the information that was on the blog. After a bit of searching I found that he made an open-source game called BL1NDSHOT where he provided the whole paint shader (although this was an extended version of the one from the blog) [https://github.com/Sergey-Shamov/BL1NDSH0T](https://github.com/Sergey-Shamov/BL1NDSH0T). It supported painting on collision, however it would require us to upgrade the project to URP or HDRP. That would not be possible, because we have some important shaders in our project that would be broken by doing that (for example the echolocation shader). This led me to look for another paint system.

### Paid assets

I found quite a few paid assets that could do what we wanted such as [Shader Painter](https://assetstore.unity.com/packages/tools/painting/shader-painter-103567),  [Paint in 3D](https://assetstore.unity.com/packages/tools/painting/paint-in-3d-26286) and [Paintz](https://assetstore.unity.com/packages/tools/particles-effects/paintz-102269#reviews). All of these were compatible with the standard pipeline, which told me that people have done this for the default pipeline before, so it was feasible for me to recreate. One thing that stroke me as odd was that Paintz only supported painting up to 4 colors on an object. After a bit of research I found that there was also a free version of it called [Paintz Free](https://assetstore.unity.com/packages/tools/paintz-free-145977#description) that supported up to 2 colors on an object. Now I was quite curious why it worked that way, and since it was a free asset I could try it out without a pay barrier blocking my way.



## Paintz Free

### Fixing Paintz Free

Before I downloaded the asset I created a separate Unity project to experiment in. Before looking into the code I tested if the system worked as I wanted. I wanted an object to leave a trail of colors on a different object while they collided. Paintz free did this quite well.

![](https://708397551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MjnIU3pkRtiaeZ6PEEt%2Fuploads%2FtgItna0XTZfjgw1yohmu%2Fpaint%20balls.gif?alt=media\&token=21d87f46-9894-48ed-9498-8327c461f2fb)

After I confirmed that it worked as I wanted, I looked into how the colors were applied to the material, which confused me quite a bit. I would've expected the colors of the ball material to be responsible for the trail color, however this was not the case. When I made the material of the orange balls green, the trail remained orange.

![](https://708397551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MjnIU3pkRtiaeZ6PEEt%2Fuploads%2FQA04mc85CI03ezgSxf6z%2Fpaintz%20balls%20wrong%20color.gif?alt=media\&token=6f773283-bc45-4dca-b743-0bb047cbb2ae)

After looking through the scripts and shaders I finally found where the color was applied. It appeared that you could select up to two colors in the material of the object that you wanted to paint (the material used the PaintStandard shader).

![](https://708397551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MjnIU3pkRtiaeZ6PEEt%2Fuploads%2FKIuryLQlAIvDWKCe7z7z%2Fpaintz%20shader.png?alt=media\&token=7ff05ab2-cd17-4131-abc7-f7dd7ef1a4df)

I was quite confused why the creator would want to limit himself to two colors on an object, so I tried to expand upon the shader myself. I replicated the process of how the first two colors were added, however the trails turned a whiteish color instead of the correct color.

After struggling with this for a while thanks to my lack of knowledge about shaders I asked Tirso for help. He has quite a bit of experience with shaders so I figured he would know what was going wrong. And I was right, with only a couple of lines of code he was able to give the trails any color that we wanted.

In the PaintStandard shader he changed the following:

```
		// Lerp the color with the splat colors based on the splat mask channels
		c.xyz = lerp(c.xyz, _SplatColor1.xyz, splatMask.x);
		c.xyz = lerp(c.xyz, _SplatColor2.xyz, splatMask.y);
```

into:

```
                // Lerp the color with the splat colors based on the splat mask channels
                  c.xyz = lerp(c.xyz, splatSDF.xyz, splatMask.a);
```



And in the Brush class he changed this:

```
    public Vector4 getMask()
    {
        if (this.splatChannel == 0) return new Vector4(1, 0, 0, 0);
        if (this.splatChannel == 1) return new Vector4(0, 1, 0, 0);
        if (this.splatChannel == 2) return new Vector4(0, 0, 1, 0);
        if (this.splatChannel == 3) return new Vector4(0, 0, 0, 1);
        return new Vector4(0, 0, 0, 0);
    }
```

into:

```
    public Color paintColor = new Color(1, 0, 1, 1);

    public Vector4 getMask()
    {
        return paintColor;
    }
```

This fixed all the limitations off Paintz Free and is actually an improvement over the paid asset as, that could only support up to 4 colors at once. Currently you're able to change the color of the trail in the inspector the paint sphere.

![](https://708397551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MjnIU3pkRtiaeZ6PEEt%2Fuploads%2FjGGAFQmiUxVqeSPwFpTM%2Fpaintz%20balls%20inspector%20color.gif?alt=media\&token=da2fd3d2-26d6-4159-af5b-0e69b47cdea4)

After we got this working I imported the package to the project, and I removed all the junk from it that we weren't gonna need. This included materials, scripts, scenes, etc., but I also cleaned up scripts that I was keeping.&#x20;

The scripts that I kept were CollisionPainter, Paint and PaintTarget. CollisionPainter enabled gameObjects to paint on other objects on collision if they had the paintTarget script. Paint contains information on how to paint and what the paint will look like.

### Expending upon Paintz Free

Once I understood how to give each individual ball an own trail cit was quite easy to make it use the material color of the object. First of I needed a reference to the meshRenderer material color of the colliding object. I expended the CollisionPainter script to send that information to the paintTarget.

```
    private void OnCollisionEnter(Collision collision)
    {
        HandleCollision(collision);
    }

    private void OnCollisionStay(Collision collision)
    {
        HandleCollision(collision);
    }

    private void HandleCollision(Collision collision)
    {
        foreach (ContactPoint contact in collision.contacts)
        {
            PaintTarget paintTarget = contact.otherCollider.GetComponent<PaintTarget>();
            if (paintTarget != null)
            {
                PaintTarget.PaintObject(paintTarget, contact.point, contact.normal, brush, GetComponent<MeshRenderer>().material.color);
            }
        }
    }
```

In the PaintObject function of the PaintTarget script I call the given reference to the meshRenderer material color "meshMaterialColor" and then I send that information to the brush with the following line:

```
        newPaint.channelMask = brush.GetPaintColor(meshMaterialColor);
```

In the Brush class (inside the Paint script) I've expended the GetPaintColor script to make the color of the trail either the material color of the colliding object or a color selected in the inspector.

```
    public Color paintColor = new Color(1, 1, 1, 0);
    public bool useMeshMaterialColor = true;
    public Vector4 GetPaintColor(Color meshMaterialColor)
    {
        if (useMeshMaterialColor)
            paintColor = meshMaterialColor;
        return paintColor;
    }
```

After I added a simple script to launch the spheres in my scene with a random force at Start the result looked like this:

![](https://708397551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MjnIU3pkRtiaeZ6PEEt%2Fuploads%2FnJQGSg4n0lybZ0JR66y7%2Fpaint%20balls%20v2.gif?alt=media\&token=fc38b0d1-e8aa-489e-a455-3734966c3eca)

## Bugs

When I had the result from the gif from above I noticed that there was a less thick trail of paint on the ground than from the previous test (I was using a new ground model in this test). After some research I concluded that that was because of a too small PaintTextureSize, after I increased it from 256x256 to 1024x2024 it looked as good as before.

Another problem that arose was that some objects weren't being painted, while they had the PaintTarget script and a collider. After some research and discussion with my team, we concluded that it was because of incorrect UV mapping on the objects. After that was corrected everything worked perfectly.

## End result

After Anouk and Tirso were done with implementing the shooting mechanic, we combined it with the painting and it's quite satisfying to see the result.

![](https://708397551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MjnIU3pkRtiaeZ6PEEt%2Fuploads%2F2xnOHotj66AdfLvOkfYR%2FShooting%20elastics2.gif?alt=media\&token=7bc266cd-50ac-43b3-8193-073f8de342ee)
