Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Removing Black Background From Custom Shaders For 2D Sprites

Discussion in 'Shaders' started by the_terrible, Apr 13, 2020.

  1. the_terrible

    the_terrible

    Joined:
    Nov 4, 2015
    Posts:
    40
    Hi all,

    I'm trying to add a blur effect to sprite images using a custom shader. I've followed this tutorial:
    https://www.ronja-tutorials.com/2018/08/27/postprocessing-blur.html

    However since I am working with 2D sprites, I lose their transparent backgrounds when I apply the shader. In fact, I've briefly tried messing with several different types of shaders and this is a common problem I keep seeing. Can anyone help me learn how to remove the black background from any custom shader? Thanks!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    Can you show examples of the problem your having, or one of your shaders?

    My best guess is you're using the wrong blend mode though. Most Sprite shaders use
    Blend One OneMinusSrcAlpha
    for their blend mode, along with multiplying the rgb by the alpha in the shader before outputting the value.
     
  3. the_terrible

    the_terrible

    Joined:
    Nov 4, 2015
    Posts:
    40
    Sorry for the late response

    Here is the code of the shader I'm using:

    Shader "Tutorial/01_Basic"
    {
    Properties{
    [HideInInspector]_MainTex ("Texture", 2D) = "white" {}
    _BlurSize("Blur Size", Range(0,0.5)) = 0
    [KeywordEnum(Low, Medium, High)] _Samples ("Sample amount", Float) = 0
    [Toggle(GAUSS)] _Gauss ("Gaussian Blur", float) = 0
    [PowerSlider(3)]_StandardDeviation("Standard Deviation (Gauss only)", Range(0.00, 0.3)) = 0.02
    }

    SubShader{
    // markers that specify that we don't need culling
    // or reading/writing to the depth buffer
    Cull Off
    ZWrite Off
    ZTest Always


    //Vertical Blur
    Pass{
    CGPROGRAM
    //include useful shader functions
    #include "UnityCG.cginc"

    //define vertex and fragment shader
    #pragma vertex vert
    #pragma fragment frag

    #pragma multi_compile _SAMPLES_LOW _SAMPLES_MEDIUM _SAMPLES_HIGH
    #pragma shader_feature GAUSS

    //texture and transforms of the texture
    sampler2D _MainTex;
    float _BlurSize;
    float _StandardDeviation;

    #define PI 3.14159265359
    #define E 2.71828182846

    #if _SAMPLES_LOW
    #define SAMPLES 10
    #elif _SAMPLES_MEDIUM
    #define SAMPLES 30
    #else
    #define SAMPLES 100
    #endif

    //the object data that's put into the vertex shader
    struct appdata{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    };

    //the data that's used to generate fragments and can be read by the fragment shader
    struct v2f{
    float4 position : SV_POSITION;
    float2 uv : TEXCOORD0;
    };

    //the vertex shader
    v2f vert(appdata v){
    v2f o;
    //convert the vertex positions from object space to clip space so they can be rendered
    o.position = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
    }

    //the fragment shader
    fixed4 frag(v2f i) : SV_TARGET{
    #if GAUSS
    //failsafe so we can use turn off the blur by setting the deviation to 0
    if(_StandardDeviation == 0)
    return tex2D(_MainTex, i.uv);
    #endif
    //init color variable
    float4 col = 0;
    #if GAUSS
    float sum = 0;
    #else
    float sum = SAMPLES;
    #endif
    //iterate over blur samples
    for(float index = 0; index < SAMPLES; index++){
    //get the offset of the sample
    float offset = (index/(SAMPLES-1) - 0.5) * _BlurSize;
    //get uv coordinate of sample
    float2 uv = i.uv + float2(0, offset);
    #if !GAUSS
    //simply add the color if we don't have a gaussian blur (box)
    col += tex2D(_MainTex, uv);
    #else
    //calculate the result of the gaussian function
    float stDevSquared = _StandardDeviation*_StandardDeviation;
    float gauss = (1 / sqrt(2*PI*stDevSquared)) * pow(E, -((offset*offset)/(2*stDevSquared)));
    //add result to sum
    sum += gauss;
    //multiply color with influence from gaussian function and add it to sum color
    col += tex2D(_MainTex, uv) * gauss;
    #endif
    }
    //divide the sum of values by the amount of samples
    col = col / sum;
    return col;
    }

    ENDCG
    }

    //Horizontal Blur
    Pass{
    CGPROGRAM
    //include useful shader functions
    #include "UnityCG.cginc"

    #pragma multi_compile _SAMPLES_LOW _SAMPLES_MEDIUM _SAMPLES_HIGH
    #pragma shader_feature GAUSS

    //define vertex and fragment shader
    #pragma vertex vert
    #pragma fragment frag

    //texture and transforms of the texture
    sampler2D _MainTex;
    float _BlurSize;
    float _StandardDeviation;

    #define PI 3.14159265359
    #define E 2.71828182846

    #if _SAMPLES_LOW
    #define SAMPLES 10
    #elif _SAMPLES_MEDIUM
    #define SAMPLES 30
    #else
    #define SAMPLES 100
    #endif

    //the object data that's put into the vertex shader
    struct appdata{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    };

    //the data that's used to generate fragments and can be read by the fragment shader
    struct v2f{
    float4 position : SV_POSITION;
    float2 uv : TEXCOORD0;
    };

    //the vertex shader
    v2f vert(appdata v){
    v2f o;
    //convert the vertex positions from object space to clip space so they can be rendered
    o.position = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
    }

    //the fragment shader
    fixed4 frag(v2f i) : SV_TARGET{
    #if GAUSS
    //failsafe so we can use turn off the blur by setting the deviation to 0
    if(_StandardDeviation == 0)
    return tex2D(_MainTex, i.uv);
    #endif
    //calculate aspect ratio
    float invAspect = _ScreenParams.y / _ScreenParams.x;
    //init color variable
    float4 col = 0;
    #if GAUSS
    float sum = 0;
    #else
    float sum = SAMPLES;
    #endif
    //iterate over blur samples
    for(float index = 0; index < SAMPLES; index++){
    //get the offset of the sample
    float offset = (index/(SAMPLES-1) - 0.5) * _BlurSize * invAspect;
    //get uv coordinate of sample
    float2 uv = i.uv + float2(offset, 0);
    #if !GAUSS
    //simply add the color if we don't have a gaussian blur (box)
    col += tex2D(_MainTex, uv);
    #else
    //calculate the result of the gaussian function
    float stDevSquared = _StandardDeviation*_StandardDeviation;
    float gauss = (1 / sqrt(2*PI*stDevSquared)) * pow(E, -((offset*offset)/(2*stDevSquared)));
    //add result to sum
    sum += gauss;
    //multiply color with influence from gaussian function and add it to sum color
    col += tex2D(_MainTex, uv) * gauss;
    #endif
    }
    //divide the sum of values by the amount of samples
    col = col / sum;
    return col;
    }

    ENDCG
    }
    }
    }


    Here is a screenshot of the sprite having the shader applied to it. I'm happy with the blur effect it is receiving. I'm unhappy with the black box background it has adopted:
    ss.png

    I am not sure what you mean by saying the blend mode I'm using is incorrect. Do you still think this is the case after seeing this code and screenshot? How to I change the blend mode?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    Ooph,
    Code (csharp):
    1. [code]
    not
    [ICODE]
    please.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    Wait ... I just realized what you’re doing with this shader. If you’re just putting that shader on a sprite, you’ve totally misunderstood how that shader is supposed to be used. It’s only intended to be used as part of a post processing effect where the output of the first pass is fed into the input of the second pass as a texture. That doesn’t work with a shader you put on an object directly, which is why the blur you’re getting is a single horizontal directional blur. Each pass only does a 1D blur.

    You either need a shader that does a 2D blur in one pass, or use those two passes to render a blurred a sprite texture into a render texture that you use instead.

    Look for a single pass blur shader.
     
    the_terrible likes this.
  7. the_terrible

    the_terrible

    Joined:
    Nov 4, 2015
    Posts:
    40
    Ah okay. Yeah I'm completely new to using shaders so I'm still trying to get it figured out. I'll try looking for something specifically for 2D then.