3
\$\begingroup\$

I want to take an (animated) 2-d sprite and apply a texture to it. Basically, I want every non-transparent pixel to be covered by the texture (color is disregarded). I understand I can probably accomplish this using masks but I am worried that may not be the most performant solution. I know you can put textures onto 3-d models, so I was wondering if there was a similar process for 2d sprites. Below I have a graphic demonstrating what I want.

Demonstration

What I have tried so far

I see there is an option to add a material to a sprite, so I tried creating various materials.

1. Default material

enter image description here

When I tried applying this material to my sprite, the sprite disappeared entirely. I suspect because this default material type only works on 3D objects

2. Sprite Material

enter image description here

I figured sprites probably expect sprite materials, so I tried creating one. However, there does not appear to be anywhere to upload an image to use as a texture. This appears to only be useful for adding tints to sprites.

\$\endgroup\$
  • 1
    \$\begingroup\$ What would make this impossible, based on your research? What have you tried so far? \$\endgroup\$ – DMGregory Feb 9 at 12:30
5
+50
\$\begingroup\$

You need to create a custom shader. The following examples are made in Unity's node based shader creation tool called Shader Graph.

You need to multiply the alpha output of your character texture (you need only to where it is) with your desired material. If you'd like to understand the operations with texture this site cover some material which is applicable in Unity too.

Quick example of the shader you are aiming for, made with ShaderGraph.

However, there does not appear to be anywhere to upload an image to use as a texture. This appears to only be useful for adding tints to sprites.

You can have properties in the shader and exposed to the inspector and edit the via script(s). In ShaderGraph you can convert variables to properties, which you can see in the "Blackboard" and assign default values as well as expose them to the editor.

TL;DR
I decided to give a basic example of the shader, created in the Shader Graph tool created by Unity.
The source project is the official ShaderGraph Example Library published by Unity Technologies on their Github repository. The sprite I applied the shader on is the "ELLEN_RUN_NoGun_01_5x5_0" asset. The grass texture sample is a random seamless texture found on google (I don't own any rights for it).

The properties on the Blackboard and wich can be controlled over a C# script are:

  1. The target sprite (collection) from the official assets provided with the project (size: 1975x2260) .
  2. The overlay texture (your custom material) (size: (1975/(number of "Ellens" we have on the X axis)*(2260/number of "Ellens" we have on the Y axis)
  3. A custom Vector2 "Tiling", which holds the information how many times the texture should be repeated on the corresponding (x and y) axis as we don't want the texture to stretch. (Assuming you'd like to use the same texture on every animated part.) The Blackboard should look like this:

    The Blackboard

    After we imported these assets it's time to created the graph. First we need to sample both sprites (passed in as properties). To our custom material we add the tiling and offset we think it is the best. Then we multiply it with the alpha of our character (we only need the pixels we can see) and put the result into the Albedo channel of the PBR Master Node.

    We leave the rest of the values on their defaults (*change the "Surface" property in the PBR settings to Opaque) except the Alpha which is going to be "0", because we don't want to see any pixel that is clipped, if the alpha is "1", every "clipped" pixel is just black.

    The Aplha clip treshold gets the inversed "pixels" of the aplha value, because we'd like to define the area the should be clipped (every pixel with the value of 1 or below should be cliped, but with an alpha value of 0, we tell that anyhing with that value shoud not be clipped).
    Here is the graph:

    Custom graph

    The generated code is a bit too long to post it here (123659 lines according to VS Code), but here is the final result of the game view:

    Final character
\$\endgroup\$
0
\$\begingroup\$

Before I even saw S4NNY1's updated answer (which seems to work perfectly!) I was able to create my own shader code by modifying the default sprite shader code (I followed this youtube video to figure out what to do).

First I looked up the default shader code (couldn't find it in my unity project, so I just googled it)

I added a '_SubTex' property to access the second texture object

Properties
{
    [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    _SubTex ("Sub Texture", 2D) = "white" {} //  This is the new property
    _Color ("Tint", Color) = (1,1,1,1)
    [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}

Then I modified the SampleSpriteTexture function to basically overlay the second texture on top of the first, using the lerp function.

        fixed4 SampleSpriteTexture (float2 uv)
        {
            fixed4 color1 = tex2D (_MainTex, uv);
            fixed4 color2 = tex2D (_SubTex, uv)

            fixed4 lerped_color = lerp(color1, color2, ceil(color1.a));

#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
            if (_AlphaSplitEnabled)
                lerped_color.a = tex2D (_AlphaTex, uv).r;
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED

            return lerped_color;
        }

And that's all it took!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.