Search Unity

Simple blend script

Discussion in 'Shaders' started by Thimble2600, Jun 15, 2019.

  1. Thimble2600

    Thimble2600

    Joined:
    Nov 27, 2015
    Posts:
    165
    Having trouble blending 2 textures together.

    I following along with Daniel_Brauer and AntonPetrov's explanations here but I've not been able to figure out how to set ShaderLab commands
    DestBlend
    and
    SrcBlend
    .

    It does sorta blend, but some colours aren't quite right.

    Here's what I'm getting: And here's what I'm hoping for:

    Would someone please tell me where I'm going wrong?


    (uv_bottomLayer) + (uv_topLayer) = (fragment)

    Code (CSharp):
    1.  
    2.             fixed4 FragmentFunc(v2f IN) : SV_Target
    3.             {
    4.                 fixed4 texture2 = SampleSpriteTexture(IN.uv_topLayer) * IN.color;
    5.                 fixed4 texture1 = SampleSpriteTexture(IN.uv_bottomLayer) * IN.color;
    6.            
    7.                 if (texture2.a > 0)
    8.                 {
    9.                     fixed4 fragment;
    10.  
    11.                     fragment.rgb = (texture1.rgb * (1 - texture2.a)
    12.                         + texture2.rgb);
    13.  
    14.                     fragment.a = (1 - (1 - texture1.a) * (1 - texture2.a));
    15.  
    16.                     return fragment;
    17.                 }
    18.                 return texture1;
    19.             }
    20.  
    21.             ENDCG
    22.  
    23.             Cull Off
    24.             Lighting Off
    25.             ZWrite Off
    26.             BlendOp Add
    27.             Blend One OneMinusSrcAlpha
     
    Last edited: Jun 16, 2019
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    When you're blending two textures together within a shader, the DestBlend and SrcBlend aren't important. That's just for how the final output color of the shader gets blended with the target. If you want to replicate traditional alpha blending, that'd just be a basic lerp.

    return lerp(texture1, texture2, texture2.a);

    However the above will cause issues with the alpha. I'd probably do something like this instead:

    return float4(lerp(texture1.rgb, texture2.rgb, texture2.a), texture1.a);
    or
    return float4(lerp(texture1.rgb, texture2.rgb, texture2.a), max(texture1.a, texture2.a));

    However, to be honest, I'm not entirely sure what kind of blend you're looking to do since the png images you posted above don't have any alpha, and I'm not sure how you produced the "wanted" image. Presumably that was done in photoshop with some specific blend mode? Which one?
     
    Last edited: Jun 17, 2019
  3. AlexHell

    AlexHell

    Joined:
    Oct 2, 2014
    Posts:
    167
    as I see, you got what you wanted.. you have yellow background and overlay blue over it

    blending code
    Code (CSharp):
    1. if (texture2.a > 0)
    2.                 {
    3.                     fixed4 fragment;
    4.                     fragment.rgb = (texture1.rgb * (1 - texture2.a)
    5.                         + texture2.rgb);
    6.                     fragment.a = (1 - (1 - texture1.a) * (1 - texture2.a));
    7.                     return fragment;
    8.                 }
    makes alpha cutout (which also can be deleted, blending in "then" already exist), then makes Normal blending
    also you use "+ texture2.rgb" which assumes your texture2.rgb is already in premultiplied-alpha mode (otherwise you need to change formula to "+ texture2.rgb * texture2.a")

    citated from https://forum.unity.com/threads/wha...-two-transparent-textures-in-a-shader.169313/
    Code (CSharp):
    1. as a common alpha-blending equation with alpha premultiplied in fragment.rgb:
    2. output = background.rgb * (1 - fragment.a) + fragment.rgb;

    this another blending is related to destination-final-step to allow chain-blend of (tex3 over (tex2 over tex1)) etc, where your original shader contains only tex1, tex2
    Code (CSharp):
    1. BlendOp Add
    2.             Blend One OneMinusSrcAlpha
    ----------

    original case from https://forum.unity.com/threads/wha...-two-transparent-textures-in-a-shader.169313/
    was
    "I need to blend three transparent textures in one shader to be drawn in one call,"
    it is important - by one call.. due to this, the solution is in-code-blend formula like
    fragment.a = (1 - (1 - texture1.a) * (1 - texture2.a));
    or tex3, tex2, tex1 formula from it

    if you don't have case "in one call" (imho you don't need)
    then you can achieve desired effect only by simple blend
    delete in-code blending
    Code (CSharp):
    1. fragment.rgb = (texture1.rgb * (1 - texture2.a)
    etc
    and simple blend shader is
    Code (CSharp):
    1. Shader "Custom/MyPremultipliedAlpha"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Main Texture", 2D) = "white" {}
    6.     }
    7.  
    8.     SubShader
    9.     {
    10.         Tags
    11.         {
    12.             "RenderType" = "Transparent"
    13.             "Queue" = "Transparent"
    14.         }
    15.  
    16.         Lighting Off
    17.         ZWrite Off
    18.         Cull Off
    19.  
    20.         Blend One OneMinusSrcAlpha
    21.  
    22.         Pass
    23.         {
    24.             CGPROGRAM
    25.  
    26.             #pragma vertex ComputeVertex
    27.             #pragma fragment ComputeFragment
    28.    
    29.             sampler2D _MainTex;
    30.    
    31.             struct VertexInput
    32.             {
    33.                 float4 vertex : POSITION;
    34.                 float2 texcoord : TEXCOORD0;
    35.             };
    36.  
    37.             struct VertexOutput
    38.             {
    39.                 float4 vertex : SV_POSITION;
    40.                 half2 texcoord : TEXCOORD0;
    41.             };
    42.    
    43.             VertexOutput ComputeVertex (VertexInput vertexInput)
    44.             {
    45.                 VertexOutput vertexOutput;
    46.        
    47.                 vertexOutput.vertex = UnityObjectToClipPos(vertexInput.vertex);
    48.                 vertexOutput.texcoord = vertexInput.texcoord;
    49.                    
    50.                 return vertexOutput;
    51.             }
    52.    
    53.             fixed4 ComputeFragment (VertexOutput vertexOutput) : SV_Target
    54.             {
    55.                 return tex2D(_MainTex, vertexOutput.texcoord);
    56.             }
    57.  
    58.             ENDCG
    59.         }
    60.     }
    61. }
    (with premult in original texture, which is important to order-independent-blend-to-background, otherwise you should change blend in it to Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha)

    which can be chained unlimited-times like
    Code (CSharp):
    1. dest = create RT..
    2. Graphics.Blit(tex1, dest, myMat)
    3. Graphics.Blit(tex2, dest, myMat)
    4. Graphics.Blit(tex3, dest, myMat)
    it produces formula
    (tex3 over (tex2 over tex1))
     
    Last edited: Jun 19, 2019