Search Unity

Separate blend modes for RGB and A not working on particle shader??

Discussion in 'Shaders' started by Kaldrax, May 26, 2021.

  1. Kaldrax

    Kaldrax

    Joined:
    Sep 14, 2017
    Posts:
    44
    Hello

    I've been playing around with shaderlab while working on this billboard particle shader, and while looking in the manual I came across the options for Blend and BlendOp to use separate blend modes for RBG and alpha channels, which seems like a really cool and useful feature, but no matter what I put into the fields it has zero effect on the alpha blend. Meaning if I put 'BlendOp Sub, Add' it'll behave the same as 'BlendOp Sub, Sub'. Am I doing something wrong? Thanks in advance.

    I'm on Unity 2019.4.4f1

    Code (CSharp):
    1.         SubShader
    2.         {
    3.         Tags {"RenderType"="Transparent" "Queue"="Transparent" }
    4.         LOD 100
    5.  
    6.         //Blend [_SrcRgb] [_DstRgb], [_SrcBlend] [_DstBlend]
    7.         BlendOp Sub, Add
    8.         Blend SrcColor DstColor, SrcAlpha DstAlpha
    9.         ZWrite Off
    10.         Cull Back
    11.    
    12.             Pass
    13.             {
    14.                 CGPROGRAM
    15.                 #pragma vertex vert
    16.                 #pragma fragment frag
    17.                 // make fog work
    18.                 #pragma multi_compile_fog
    19.    
    20.                 #include "UnityCG.cginc"
    21.    
    22.                 struct appdata
    23.                 {
    24.                     float4 vertex : POSITION;
    25.                     float2 uv : TEXCOORD0;
    26.                     float3 particlePosition : TEXCOORD1;
    27.                     float4 color : COLOR;
    28.                 };
    29.    
    30.                 struct v2f
    31.                 {
    32.                     float4 pos : SV_POSITION;
    33.                     float2 uv : TEXCOORD0;
    34.                     float4 color : COLOR;
    35.                     UNITY_FOG_COORDS(1)
    36.                 };
    37.    
    38.                 sampler2D _MainTex;
    39.                 float4 _MainTex_ST;
    40.    
    41.                 v2f vert (appdata v)
    42.                 {
    43.                     v2f o;
    44.                     o.color = v.color;
    45.                     #ifdef UNITY_SINGLE_PASS_STEREO
    46.                     float3 camPos = (unity_StereoWorldSpaceCameraPos[0] + unity_StereoWorldSpaceCameraPos[1]) * 0.5;
    47.                     #else
    48.                     float3 camPos = _WorldSpaceCameraPos;
    49.                     #endif
    50.  
    51.                     float3 worldPivot = v.particlePosition.xyz;
    52.    
    53.                     float3 scale = float3(
    54.                         length(unity_ObjectToWorld._m00_m01_m02),
    55.                         length(unity_ObjectToWorld._m10_m11_m12),
    56.                         1.0
    57.                         );
    58.  
    59.                     float3 f = normalize(lerp(
    60.                         -UNITY_MATRIX_V[2].xyz, // view forward dir
    61.                         normalize(worldPivot - camPos), // camera to pivot dir
    62.                         _CameraFacing));
    63.                     float3 u = float3(0.0,1.0,0.0);
    64.                     float3 r = normalize(cross(u, f));
    65.                     u = -normalize(cross(r, f));
    66.                     float3x3 billboardRotation = float3x3(r, u, f);
    67.                     float3 worldPos = mul((v.vertex.xyz - worldPivot) * scale, billboardRotation) + worldPivot;
    68.                     o.pos = UnityWorldToClipPos(worldPos);
    69.    
    70.                     o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    71.                     UNITY_TRANSFER_FOG(o,o.pos);
    72.                     return o;
    73.                 }
    74.    
    75.                 fixed4 frag (v2f i) : SV_Target
    76.                 {
    77.                     // sample the texture
    78.                     fixed4 col = tex2D(_MainTex, i.uv);
    79.                     col *= i.color;
    80.                     // apply fog
    81.                     //UNITY_APPLY_FOG(i.fogCoord, col.rgb);
    82.                     return col;
    83.                 }
    84.                 ENDCG
    85.             }
    86.         }
    87.  
     
  2. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,028
    This should work. What HW are you testing on?
     
  3. Kaldrax

    Kaldrax

    Joined:
    Sep 14, 2017
    Posts:
    44
    I'm on an Intel i7 and a GTX 1660TI
     
  4. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,028
    Can you please share the screenshots of your tests?
    Preferably, side-by-side with `BlendOp Sub, Sub` and `BlendOp Sub, Add` :)
    Thank you!
     
  5. Kaldrax

    Kaldrax

    Joined:
    Sep 14, 2017
    Posts:
    44
    Sure thing.

     
  6. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,028
    Seems like a bug - can you please submit a bug report?
    Thank you :)
     
  7. Kaldrax

    Kaldrax

    Joined:
    Sep 14, 2017
    Posts:
    44
    Sure thing
     
    aleksandrk likes this.
  8. ekakiya

    ekakiya

    Joined:
    Jul 25, 2011
    Posts:
    79
    Separate blend mode doesn't change the alpha value for current alpha blend, it changes the value written to the screen buffer's alpha channel.

    Typical alpha blending is
    BlendOp Add
    Blend SrcAlpha OneMinusSrcAlpha

    that means
    new screenBuffer.rgba = shaderOutput.rgba * shaderOutput.a + screenBuffer.rgba * (1.0 - shaderOutput.a)

    The Separate blend mode
    BlendOp Sub, Add
    Blend SrcColor DstColor, SrcAlpha DstAlpha

    that means
    new screenBuffer.rgb = shaderOutput.rgb * shaderOutput.rgb - screenBuffer.rgb * screenBuffer.rgb
    new screenBuffer.a = shaderOutput.a * shaderOutput.a + screenBuffer.a * screenBuffer.a
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Yeah, I concur with @ekakiya , the above examples are working as intended. Modifying the alpha blend op should not affect the RGB. There's no bug here.
     
  10. Kaldrax

    Kaldrax

    Joined:
    Sep 14, 2017
    Posts:
    44
    Maybe I'm not understanding what's going on here then. Isn't the alpha value used to determine how much of the dst color to blend? I thought I could use this to keep the same particle shape but subtract values from the rgb.
     
    Ruhan-_- likes this.
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Let's break down what @ekakiya said above.

    The traditional alpha blend is:
    Code (csharp):
    1. BlendOp Add
    2. Blend SrcAlpha OneMinusSrcAlpha
    You don't actually need the
    BlendOp Add
    in the shader, because
    BlendOp Add
    is the default. But we'll get back to that in a second and focus on
    Blend
    for now.

    The two parameters after the
    Blend
    are the Src factor and Dst factor, in that order. The Src (source) being the output of the shader, and Dst (destination) being the value current held in the render target. The factors of
    SrcAlpha
    and
    OneMinusSrcAlpha
    are what they look like, the alpha value output by the shader. Those values are used to multiply the Src and the Dst.

    The
    BlendOp
    determines what is done with the two resulting values. In the default case of
    BlendOp Add
    , it adds them together.

    Note, there's no commas here. Only one "operation" after the
    BlendOp
    and two "factors" after the
    Blend
    .

    All that together tells the math the GPU to use to blend the output of the shader with the frame buffer.
    Code (csharp):
    1. // Blend                                SrcAlpha                             OneMinusSrcAlpha
    2. //                  SrcColor            v                DstColor            v
    3. screenBuffer.rgba = shaderOutput.rgba * shaderOutput.a + screenBuffer.rgba * (1.0 - shaderOutput.a)
    4. //                                                    ^
    5. //                                                    BlendOp Add
    The chosen
    Blend
    factors multiply all four RGBA values of the source and destination, and they're combined using the
    BlendOp
    operation.


    When you use the comma in the
    BlendOp
    and
    Blend
    you're saying you want to do different math to determine the values being written to the RGB color vs alpha of the render target.

    So, if we make a simple change like
    BlendOp Add, Sub
    , all this does is change that
    +
    in the middle of the math function to a
    -
    , but only for the value being written to the alpha channel of the render target.
    Code (csharp):
    1. screenBuffer.rgba = float4(
    2. //                                    BlendOp Add
    3. // Blend               SrcAlpha       |                    OneMinusSrcAlpha
    4. //  SrcColor           v              v                    v
    5.     shaderOutput.rgb * shaderOutput.a + screenBuffer.rgb * (1.0 - shaderOutput.a),
    6.     shaderOutput.a   * shaderOutput.a - screenBuffer.a   * (1.0 - shaderOutput.a)
    7. //  SrcAlpha           ^              ^                    ^
    8. // Blend               SrcAlpha       |                    OneMinusSrcAlpha
    9. //                                    BlendOp Sub
    10. );
    Again, this only changes the value that's written to the alpha channel of the render target. The blend being used for the RGB color value is not affected.
     
    Last edited: May 28, 2021
    lilacsky824 and FM-Productions like this.
  12. Kaldrax

    Kaldrax

    Joined:
    Sep 14, 2017
    Posts:
    44
    Ah okay I understand now. Thanks for explaining it so well!
     
  13. fabrism77

    fabrism77

    Joined:
    May 5, 2019
    Posts:
    12
    i don't.. what does it change? I don't see anything different. How does it change alpha of the render target?