7
\$\begingroup\$

Recently I've been toying around with neon-ish effects for a game I'm making and wanted to share my results with you.

If you guys have any other methods of achieving this result, please be sure to share.

\$\endgroup\$
10
\$\begingroup\$

To achieve this effect my friend has created a custom sprite, we played around with all sorts of sprites and you can customize this part to your liking. The shader I've written for this effect, takes in the sprite, makes the sprite grayscale, uses the color to make a colored version and uses the grayscale version together with the colored version to color the parts that are gray.

First of all, your sprite doesn't really need to be grayscale, but it will probably be better for grayscale sprites. The trail effect that I've used stretches the sprite horizontally, so making the sprite horizontal is quite necessary.

This is the sprite we've used in game (it has built in alpha which is necessary for the glow effect): enter image description here

This is the shader I've made for the effect:

Shader "Trail/Neon"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
    _Color("Tint", Color) = (1,1,1,1)
        _MainTexture("Sprite", 2D) = "white" {}
    [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
    }

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

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile _ PIXELSNAP_ON
#pragma multi_compile _ ETC1_EXTERNAL_ALPHA
#include "UnityCG.cginc"

        struct appdata_t
    {
        float4 vertex   : POSITION;
        float4 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
    };

    fixed4 _Color;

    v2f vert(appdata_t IN)
    {
        v2f OUT;
        UNITY_SETUP_INSTANCE_ID(IN);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
        OUT.vertex = UnityObjectToClipPos(IN.vertex);
        OUT.texcoord = IN.texcoord;
        OUT.color = IN.color * _Color;
#ifdef PIXELSNAP_ON
        OUT.vertex = UnityPixelSnap(OUT.vertex);
#endif

        return OUT;
    }

    sampler2D _MainTexture;
    sampler2D _AlphaTex;

    fixed4 SampleSpriteTexture(float2 uv)
    {
        fixed4 color = tex2D(_MainTexture, uv);

#if ETC1_EXTERNAL_ALPHA
        // get the color from an external texture (usecase: Alpha support for ETC1 on android)
        color.a = tex2D(_AlphaTex, uv).r;
#endif //ETC1_EXTERNAL_ALPHA

        return color;
    }

    fixed4 frag(v2f IN) : SV_Target
    {
        //standard sprite shader, only this part is different.
        //takes the sprite in
        fixed4 s = SampleSpriteTexture(IN.texcoord);

        //makes a colored version of the sprite
        fixed4 c = s * IN.color;

        //makes the grayscale version
        fixed n = (s.g + s.r + s.b) / 3;

        //So, I've scrapped the previous calculation in favor of this I'll leave the previous one in too, just for reference
        c = (c*3 + n*c.a)*c.a;
        //Adds the grayscale version on top of the colored version
        //The alpha multiplications give the neon effect feeling
        //c.g = (c.g + n * c.a) * c.a;
        //c.r = (c.r + n * c.a) * c.a;
        //c.b = (c.b + n * c.a) * c.a;
        // You can add c.a multiplications of colors 
        //(i.e. turn  c.g to c.g*c.a) for less color in your effect
        //this saturates the insides a bit too much for my liking

        return c;
    }
        ENDCG
    }
    }
}

You will have to make separate materials for each different color because of how Unity works with shaders (if there's a way to overcome this, please do tell).

So, you make a new material, pick the shader for that material, change the color and the sprite input, and you've got yourself a neon-like effect.

enter image description here

And the effect achieved in a gif:

enter image description here

Effect on different sprites:

enter image description here

\$\endgroup\$
  • \$\begingroup\$ Thank you for sharing this. Shader programming is not my area of expertise, but is it really necessary to recalculate the value of n for each texel again and again on every single frame? Wouldn't it be faster to somehow convert the texture to a one-byte-per-pixel format when it is loaded and use that one instead? \$\endgroup\$ – Philipp Feb 27 '17 at 9:51
  • \$\begingroup\$ I've looked into the matter and couldn't find a better way to implement this. I was already using the built in shader's calculations, just changed a few numbers around, so it shouldn't have a bigger effect on performance than a standard shader. (I'm still not sure about the pass but I think you're right on that front, it's per frame rather than once.) I'm deleting my other comments because this is getting too close to a discussion :) @Philipp \$\endgroup\$ – John Hamilton Feb 27 '17 at 10:54
  • \$\begingroup\$ I tried using your shader and could get it to work with moderate success. As I want to use my own sprites I experimented with some different setting in Photoshop, which didn't really work out I have trouble understanding this section of your post regarding the creation of own sprites with your shader: "First of all, your sprite doesn't really need to be grayscale..." can you explain in more detail how the sprite should be prepared to work with this shader? \$\endgroup\$ – JustIlom May 21 '18 at 15:35
  • \$\begingroup\$ @JustIlom This shader pretty much colors the sprite you have and gives the alpha value more power over what the sprite will look like. Try the shader with a gradient effect that goes from white with full alpha to black with zero alpha (you will need to save it in a format that supports alpha values), it will be more apparent what it does. (The example sprite I've included at the top should work with this but I don't know if imgur has changed the alpha values.) \$\endgroup\$ – John Hamilton May 22 '18 at 5:39
  • \$\begingroup\$ John Hamilton,Does this shader work in a line renderer or is it just in sprites? \$\endgroup\$ – Nitecki Mar 28 at 4:42

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