2
\$\begingroup\$

I have a GameObject that looks like this:

enter image description here

When I change the "color" value in the GameObject's SpriteRenderer to be (255, 0, 0, 255), the GameObject ends up looking like this:

enter image description here

But I want it to look this like instead:

enter image description here

What methods can I use to accomplish this?

My goal is to have it be dynamic so that it can range anywhere from "0% red" (the original colors) to "100% red" (entirely red, except for the transparent pixels).

For example, I want 50% red to look roughly like this:

enter image description here

\$\endgroup\$
2
  • \$\begingroup\$ Which renderer are you using? Built-in, URP, or HDRP? \$\endgroup\$
    – DMGregory
    Commented Mar 10, 2023 at 11:34
  • \$\begingroup\$ @DMGregory - I'm using URP. \$\endgroup\$ Commented Mar 10, 2023 at 12:13

3 Answers 3

3
\$\begingroup\$

After much trial and error, I was able to achieve what I wanted by modifing Unity's default "GUI/Text Shader" shader, which I downloaded from here under "Downloads (Win)" > "Built in shaders".

I have very little idea how shaders work, but here's my crude explanation of what I think is happening in the shader code below:

  1. It stores the original pixel art in a variable named fixed4 sprite_texture
  2. It stores a solid colored copy of the pixel art, painted with the SpriteRenderer's current color, in a variable named fixed4 solid_color
  3. It merges the two variables together using lerp, with the SpriteRenderer's current color's alpha value being used to determine how much the original pixel art should be colorized.

Once that shader has been applied to a GameObject's SpriteRenderer's material, I can easily change the GameObject's color through its MonoBehavior code.

Usage example (C#):

"0% red":

this.sprite_renderer.color = new Color(1f, 0f, 0f, 0f);

"50% red":

this.sprite_renderer.color = new Color(1f, 0f, 0f, .5f);

"100% red":

this.sprite_renderer.color = new Color(1f, 0f, 0f, 1f);

I'm not sure if there any drawbacks to this approach, but from what I can tell, it's easy to use, supports all colors (including pure white and black), and supports pixel art with a transparent background, which is exactly what I wanted.

Usage example (GUI):

enter image description here

Full shader code:

Shader "DynamicColorShader"
{
    Properties
    {
        _MainTex ("Font Texture", 2D) = "white" {}
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
        }

        Lighting Off Cull Off ZTest Always ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ UNITY_SINGLE_PASS_STEREO STEREO_INSTANCING_ON STEREO_MULTIVIEW_ON
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _MainTex;
            uniform float4 _MainTex_ST;
            uniform fixed4 _Color;

            v2f vert (appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = v.color;
                o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
                return o;
            }

            fixed4 frag (v2f IN) : SV_Target
            {
                fixed4 sprite_texture = tex2D (_MainTex, IN.texcoord);

                fixed4 solid_color = IN.color;
                solid_color.a *= tex2D(_MainTex, IN.texcoord).a;

                fixed4 final = lerp(sprite_texture, solid_color, solid_color.a);

                return final;
            }
            ENDCG
        }
    }
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ Seems reasonable. \$\endgroup\$ Commented Mar 10, 2023 at 17:13
  • 1
    \$\begingroup\$ If you are a bit confused about shaders: the shader runs once per pixel so variables like sprite_texture do not store a whole copy of the sprite, rather they store just the colour in the sprite for the current pixel. \$\endgroup\$ Commented Mar 10, 2023 at 17:14
  • 1
    \$\begingroup\$ Welcome to the exciting world of shader programming. \$\endgroup\$
    – Philipp
    Commented Mar 14, 2023 at 14:37
  • \$\begingroup\$ One remark for people willing to use this to "overlap" a color over an item: the above shader will apply alpha channel (transparency) to the final sprite. If you want your sprite to be completely solid (e.g. no see thru) you must add "final.a= sprite_texture.a;" just before "return final;" \$\endgroup\$ Commented Nov 5, 2023 at 20:44
0
\$\begingroup\$

The "color" parameter on a sprite renderer is applied as a tint, multiplied by the original texture colour. So it can only ever make colours darker in each of the red/green/blue channels — it can't "add red" where there was no red before.

If you don't want to write a custom shader, you can also just make a version of this sprite texture that has the same opacity (the same pattern of transparent and non-transparent pixels), but where all the pixel colours are white (255, 255, 255), which is (1f, 1f, 1f) when normalized. One multiplied by any other number is just that other number itself, so this lets you get your tint colour to output directly as the display colour.

If you sometimes want the multicolour version and sometimes the plain red version, you can swap the sprite renderer's sprite property at the same time that you change the tint colour.

\$\endgroup\$
3
  • \$\begingroup\$ My goal was to have it be dynamic so that it can range anywhere from "0% red" (the original colors) to "100% red" (entirely red, except for the transparent pixels). A custom shader would be fine, but I looked into them and they seem complex and difficult to debug. Do you know of any simple custom shaders that would do what I'm looking for? The behavior I'm looking for is comparable to several common "blend modes" in programs like GIMP, Inkscape, Photoshop, etc, so I would hope that it's not too complex. \$\endgroup\$ Commented Mar 9, 2023 at 18:36
  • 1
    \$\begingroup\$ If blending smoothly between these two states is a requirement, it should have been stated in your question from the beginning, otherwise users won't know to give you answers that do that. This is not overly complicated to implement — about 3 lines of code to change in the default sprite shader, or maybe 6 nodes to wire up in shader graph — but I don't have time to write a second answer just now. To avoid that issue in future, make sure your question is written so that the first answer is able to cover what you need, without secret requirements that demand follow-up. \$\endgroup\$
    – DMGregory
    Commented Mar 9, 2023 at 18:52
  • \$\begingroup\$ Sorry, I didn't realize that it wasn't clear enough. I've added the additional detail to the original question. \$\endgroup\$ Commented Mar 9, 2023 at 19:28
-1
\$\begingroup\$

You don't want to change the SpriteRenderer's color, but its shader (find it in SpriteRenderer.material.shader), so that it reduces any underlying image colour to one predefined colour.

You may check this: https://answers.unity.com/questions/1546026/shader-to-set-sprite-to-solid-color.html

And this: https://answers.unity.com/questions/582145/is-there-a-way-to-set-a-sprites-color-solid-white.html

\$\endgroup\$
1
  • \$\begingroup\$ Can you please provide the answer to the question in your answer directly? Pointing people to other websites isn't a future-proof way to answer, because those links might break sooner or later. The second link you posted is already only marginally useful, because it suffers from the $$anonymous$$ bug on Unity Answers. \$\endgroup\$
    – Philipp
    Commented Mar 10, 2023 at 12:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .