Search Unity

How to make overlapping sprites additively blend without alpha?

Discussion in 'Shaders' started by LiterallyJeff, Jun 14, 2017.

  1. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Hello,

    I've been trying to figure out how to make this type of shader, that when applied to three circle sprites, could result in this visual (forgive the low quality image):


    So far I've tried to create a shader with 3 passes using the Stencil buffer and two Grab Passes in between, but it feels clunky and strangely difficult to get right.

    In my mind the order that the objects render would not have any influence on the end result, yet my experiments have all shown that the sorting order will affect the outcome (so I think I'm doing something wrong).

    Here's what I've got so far:
    Code (CSharp):
    1. Shader "Custom/Sprites/ColorMix"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData]_MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags
    10.         {
    11.             "Queue" = "Transparent"
    12.             "RenderType" = "Transparent"
    13.             "IgnoreProjector" = "True"
    14.             "PreviewType" = "Plane"
    15.             "CanUseSpriteAtlas" = "True"
    16.         }
    17.  
    18.         Cull Off
    19.         Lighting Off
    20.         ZWrite Off
    21.  
    22.         Pass
    23.         {
    24.             Stencil
    25.             {
    26.                 Ref 0
    27.                 Comp Equal
    28.                 Pass IncrSat
    29.             }
    30.            
    31.  
    32.             CGPROGRAM
    33.             #pragma vertex vert
    34.             #pragma fragment frag
    35.            
    36.             #include "UnityCG.cginc"
    37.  
    38.             struct appdata
    39.             {
    40.                 float4 vertex : POSITION;
    41.                 float2 uv : TEXCOORD0;
    42.                 float4 color : COLOR;
    43.             };
    44.  
    45.             struct v2f
    46.             {
    47.                 float4 vertex : SV_POSITION;
    48.                 float2 uv : TEXCOORD0;
    49.                 float4 color : COLOR;
    50.             };
    51.  
    52.             sampler2D _MainTex;
    53.             float4 _MainTex_ST;
    54.  
    55.             v2f vert (appdata input)
    56.             {
    57.                 v2f output;
    58.                 output.vertex = UnityObjectToClipPos(input.vertex);
    59.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    60.                 output.color = input.color;
    61.                 return output;
    62.             }
    63.            
    64.             fixed4 frag (v2f input) : SV_Target
    65.             {
    66.                 fixed4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    67.                 return textureColor;
    68.             }
    69.             ENDCG
    70.         }
    71.  
    72.         GrabPass{ "_GrabPass" }
    73.  
    74.         Pass
    75.         {
    76.  
    77.             Stencil
    78.             {
    79.                 Ref 1
    80.                 Comp Equal
    81.                 Pass IncrSat
    82.             }
    83.  
    84.             CGPROGRAM
    85.             #pragma vertex vert
    86.             #pragma fragment frag
    87.  
    88.             #include "UnityCG.cginc"
    89.  
    90.             struct appdata
    91.             {
    92.                 float4 vertex : POSITION;
    93.                 float2 uv : TEXCOORD0;
    94.                 float4 color : COLOR;
    95.             };
    96.  
    97.             struct v2f
    98.             {
    99.                 float2 uv : TEXCOORD0;
    100.                 float4 grabPos : TEXCOORD1;
    101.                 float4 vertex : SV_POSITION;
    102.                 float4 color : COLOR;
    103.             };
    104.  
    105.             sampler2D _MainTex;
    106.             float4 _MainTex_ST;
    107.             sampler2D _GrabPass;
    108.  
    109.             v2f vert(appdata input)
    110.             {
    111.                 v2f output;
    112.                 output.vertex = UnityObjectToClipPos(input.vertex);
    113.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    114.                 output.grabPos = ComputeGrabScreenPos(output.vertex);
    115.                 output.color = input.color;
    116.                 return output;
    117.             }
    118.  
    119.             fixed4 frag(v2f input) : SV_Target
    120.             {
    121.                 fixed4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    122.                 fixed4 backgroundColor = tex2Dproj(_GrabPass, input.grabPos);
    123.  
    124.                 fixed4 outputColor = textureColor + backgroundColor;
    125.                 return outputColor;
    126.             }
    127.             ENDCG
    128.         }
    129.  
    130.         GrabPass{ "_GrabPass2" }
    131.  
    132.         Pass
    133.         {
    134.             Stencil
    135.             {
    136.                 Ref 2
    137.                 Comp Equal
    138.             }
    139.  
    140.             CGPROGRAM
    141.             #pragma vertex vert
    142.             #pragma fragment frag
    143.  
    144.             #include "UnityCG.cginc"
    145.  
    146.             struct appdata
    147.             {
    148.                 float4 vertex : POSITION;
    149.                 float2 uv : TEXCOORD0;
    150.                 float4 color : COLOR;
    151.             };
    152.  
    153.             struct v2f
    154.             {
    155.                 float2 uv : TEXCOORD0;
    156.                 float4 grabPos : TEXCOORD1;
    157.                 float4 vertex : SV_POSITION;
    158.                 float4 color : COLOR;
    159.             };
    160.  
    161.             sampler2D _MainTex;
    162.             float4 _MainTex_ST;
    163.             sampler2D _GrabPass2;
    164.  
    165.             v2f vert(appdata input)
    166.             {
    167.                 v2f output;
    168.                 output.vertex = UnityObjectToClipPos(input.vertex);
    169.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    170.                 output.grabPos = ComputeGrabScreenPos(output.vertex);
    171.                 output.color = input.color;
    172.                 return output;
    173.             }
    174.  
    175.             fixed4 frag(v2f input) : SV_Target
    176.             {
    177.                 fixed4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    178.                 fixed4 backgroundColor = tex2Dproj(_GrabPass2, input.grabPos);
    179.  
    180.                 fixed4 outputColor = textureColor + backgroundColor;
    181.                 return outputColor;
    182.             }
    183.             ENDCG
    184.         }
    185.     }
    186. }
    187.  
    If anyone knows how this might be done, or can see any mistakes in my code, please let me know!

    Thanks
     
    Last edited: Jun 14, 2017
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,349
    Any reason to not just use Blend One One?
     
  3. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Well the main reason was because I didn't want to have that blend mode for the initial pass so that it wouldn't blend with the background. But I just realized I can do a different blend mode per-pass and only blend additively for overlaps.

    So with that, I can get rid of the grab passes and third pass. I was definitely overthinking it.

    Capture.PNG

    Code (CSharp):
    1. Shader "Custom/Sprites/ColorMix"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData]_MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags
    10.         {
    11.             "Queue" = "Transparent"
    12.             "RenderType" = "Transparent"
    13.             "IgnoreProjector" = "True"
    14.             "PreviewType" = "Plane"
    15.             "CanUseSpriteAtlas" = "True"
    16.         }
    17.  
    18.         Cull Off
    19.         Lighting Off
    20.         ZWrite Off
    21.         ColorMask RGBA
    22.  
    23.         Pass
    24.         {
    25.             // if the value in the buffer is 0, run this pass and increment the buffer
    26.             Stencil
    27.             {
    28.                 Ref 0
    29.                 Comp Equal
    30.                 Pass IncrSat
    31.             }
    32.        
    33.             // default blending for first pass
    34.             Blend SrcAlpha OneMinusSrcAlpha
    35.        
    36.             CGPROGRAM
    37.             #pragma vertex vert
    38.             #pragma fragment frag
    39.        
    40.             #include "UnityCG.cginc"
    41.  
    42.             struct appdata
    43.             {
    44.                 float4 vertex : POSITION;
    45.                 float2 uv : TEXCOORD0;
    46.                 float4 color : COLOR;
    47.             };
    48.  
    49.             struct v2f
    50.             {
    51.                 float4 vertex : SV_POSITION;
    52.                 float2 uv : TEXCOORD0;
    53.                 float4 color : COLOR;
    54.             };
    55.  
    56.             sampler2D _MainTex;
    57.             float4 _MainTex_ST;
    58.  
    59.             v2f vert (appdata input)
    60.             {
    61.                 v2f output;
    62.                 output.vertex = UnityObjectToClipPos(input.vertex);
    63.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    64.                 output.color = input.color;
    65.                 return output;
    66.             }
    67.        
    68.             fixed4 frag (v2f input) : SV_Target
    69.             {
    70.                 fixed4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    71.                 return textureColor;
    72.             }
    73.             ENDCG
    74.         }
    75.  
    76.         Pass
    77.         {
    78.             // if the value in the buffer is 1, run this pass
    79.             Stencil
    80.             {
    81.                 Ref 1
    82.                 Comp Equal
    83.             }
    84.  
    85.             // additively blend any overlaps
    86.             Blend One One
    87.  
    88.             CGPROGRAM
    89.             #pragma vertex vert
    90.             #pragma fragment frag
    91.  
    92.             #include "UnityCG.cginc"
    93.  
    94.             struct appdata
    95.             {
    96.                 float4 vertex : POSITION;
    97.                 float2 uv : TEXCOORD0;
    98.                 float4 color : COLOR;
    99.             };
    100.  
    101.             struct v2f
    102.             {
    103.                 float2 uv : TEXCOORD0;
    104.                 float4 vertex : SV_POSITION;
    105.                 float4 color : COLOR;
    106.             };
    107.  
    108.             sampler2D _MainTex;
    109.             float4 _MainTex_ST;
    110.  
    111.             v2f vert(appdata input)
    112.             {
    113.                 v2f output;
    114.                 output.vertex = UnityObjectToClipPos(input.vertex);
    115.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    116.                 output.color = input.color;
    117.                 return output;
    118.             }
    119.  
    120.             fixed4 frag(v2f input) : SV_Target
    121.             {
    122.                 fixed4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    123.                 return textureColor;
    124.             }
    125.             ENDCG
    126.         }
    127.     }
    128. }
     
    Last edited: Jun 16, 2017
    fm_andi likes this.
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    @bgolus Maybe you can help me with a follow up question.

    So this technique works really nicely, but for certain color combinations it doesn't do what I'm looking for.

    For instance, overlapping a circle of pure Yellow, and a circle of pure Red has no visible effect, because pure Yellow already has R=1 so adding more Red just keeps it Yellow.

    How could I approach this situation where I want Yellow overlapping Red to create Orange? I don't think that can be achieved through additive blend mode alone, correct?
     
    Last edited: Jun 16, 2017
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,349
    Real time computer graphics are done in RGB space, which is an additive color system, thus red + yellow == yellow because yellow (1,1,0) == red (1,0,0) + green (0,1,0). Unless you're rendering in HDR, it'll just clamp to 1,1,0 if you add more red or green.

    Red + Yellow = Orange is using the subtractive color system everyone learned in school of RYB (which in reality are the secondary colors of the CMY color model!), but Orange in RGB is 1,0.5,0. What you need to get orange is to get the average of red (1,0,0) + yellow (1,1,0), which is easy enough to do with a traditional alpha blend with a fixed alpha of 0.5, but that only works for the color mixing of a primary RGB and secondary RGB (yellow, cyan, magenta), it doesn't work for blending against a white or black background, or even with two primary RGB colors!

    Basically, to do what you want you have to do all the blending manually in the shader, and to do that means having to render your sprites using grab passes, or render textures and blit(), so you can read the current pixel color, convert it to CMY or HSV, do your blend, and convert back. It also means you have to disable batching and not use sprites to ensure each "sprite" quad gets it's own unique grab pass.
     
    LiterallyJeff likes this.
  6. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Thank you so much for taking the time to explain all this for me. It makes a lot of sense. I was trying to take a similar approach before, but I wasn't sure exactly why, or if I was simply overlooking something. This is great info, and gives me a lot to experiment with. HSV conversion and averaging hues seems like the way to go.
     
  7. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    keyword: Colorspaces
    Good luck because that's a damn rabbit hole
     
  8. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Well, by modifying some of my previous attempt and averaging HSV colors, it seems to be working for a single overlap.

    Capture2.PNG

    I'm not sure how to get this to support more overlaps. It seems like the GrabPass is perhaps not picking up previously blended pixels. I've added a "DisableBatching" = "True" tag to the shader, but that has no visible effect.

    Any suggestions are welcome.

    Code (CSharp):
    1. Shader "Custom/Sprites/ColorMixAverage"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData]_MainTex("Texture", 2D) = "white" {}
    6.     }
    7.  
    8.     SubShader
    9.     {
    10.         Tags
    11.         {
    12.             "Queue" = "Transparent"
    13.             "RenderType" = "Transparent"
    14.             "IgnoreProjector" = "True"
    15.             "PreviewType" = "Plane"
    16.             "CanUseSpriteAtlas" = "True"
    17.             "DisableBatching" = "True"
    18.         }
    19.  
    20.         Cull Off
    21.         Lighting Off
    22.         ZWrite Off
    23.         Blend SrcAlpha OneMinusSrcAlpha
    24.  
    25.         Pass
    26.         {
    27.             Stencil
    28.             {
    29.                 Ref 0
    30.                 Comp Equal
    31.                 Pass IncrSat
    32.             }
    33.  
    34.  
    35.             CGPROGRAM
    36.             #pragma vertex vert
    37.             #pragma fragment frag
    38.  
    39.             #include "UnityCG.cginc"
    40.  
    41.             struct appdata
    42.             {
    43.                 float4 vertex : POSITION;
    44.                 float2 uv : TEXCOORD0;
    45.                 float4 color: COLOR;
    46.             };
    47.  
    48.             struct v2f
    49.             {
    50.                 float4 vertex : SV_POSITION;
    51.                 float2 uv : TEXCOORD0;
    52.                 float4 color: COLOR;
    53.             };
    54.  
    55.             sampler2D _MainTex;
    56.             float4 _MainTex_ST;
    57.  
    58.             v2f vert(appdata input)
    59.             {
    60.                 v2f output;
    61.                 output.vertex = UnityObjectToClipPos(input.vertex);
    62.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    63.                 output.color = input.color;
    64.                 return output;
    65.             }
    66.  
    67.             fixed4 frag(v2f input) : SV_Target
    68.             {
    69.                 fixed4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    70.                 return textureColor;
    71.             }
    72.             ENDCG
    73.         }
    74.  
    75.         GrabPass{"_GrabPass"}
    76.  
    77.         Pass
    78.         {
    79.             Stencil
    80.             {
    81.                 Ref 0
    82.                 Comp NotEqual
    83.             }
    84.  
    85.             CGPROGRAM
    86.             #pragma vertex vert
    87.             #pragma fragment frag
    88.  
    89.             #include "UnityCG.cginc"
    90.  
    91.             struct appdata
    92.             {
    93.                 float4 vertex : POSITION;
    94.                 float2 uv : TEXCOORD0;
    95.                 float4 color : COLOR;
    96.             };
    97.  
    98.             struct v2f
    99.             {
    100.                 float4 vertex : SV_POSITION;
    101.                 float2 uv : TEXCOORD0;
    102.                 float4 grabPos : TEXCOORD1;
    103.                 float4 color : COLOR;
    104.             };
    105.  
    106.             //https://github.com/greggman/hsva-unity/blob/master/Assets/Shaders/HSVRangeShader.shader
    107.             float3 rgb2hsv(float3 c) {
    108.                 float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    109.                 float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
    110.                 float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
    111.  
    112.                 float d = q.x - min(q.w, q.y);
    113.                 float e = 1.0e-10;
    114.                 return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
    115.             }
    116.  
    117.             //https://github.com/greggman/hsva-unity/blob/master/Assets/Shaders/HSVRangeShader.shader
    118.             float3 hsv2rgb(float3 c) {
    119.                 c = float3(c.x, clamp(c.yz, 0.0, 1.0));
    120.                 float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    121.                 float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
    122.                 return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
    123.             }
    124.  
    125.             sampler2D _MainTex;
    126.             float4 _MainTex_ST;
    127.             sampler2D _GrabPass;
    128.  
    129.             v2f vert(appdata input)
    130.             {
    131.                 v2f output;
    132.                 output.vertex = UnityObjectToClipPos(input.vertex);
    133.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    134.                 output.grabPos = ComputeGrabScreenPos(output.vertex);
    135.                 output.color = input.color;
    136.                 return output;
    137.             }
    138.  
    139.             fixed4 frag(v2f input) : SV_Target
    140.             {
    141.                 float4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    142.                 float4 backgroundColor = tex2Dproj(_GrabPass, input.grabPos);
    143.  
    144.                 float3 textureColorHSV = rgb2hsv(textureColor);
    145.                 float3 backgroundColorHSV = rgb2hsv(backgroundColor);
    146.  
    147.                 float3 averaged = (textureColorHSV + backgroundColorHSV) * 0.5f;
    148.                 float4 result = float4(hsv2rgb(averaged), 1.0);
    149.                 return result;
    150.             }
    151.             ENDCG
    152.         }
    153.     }
    154. }
     
    Last edited: Jun 16, 2017
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,349
  10. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    If you are going for custom color space, why not use a look up texture that basically show how color are mixed? Ie main color are on edge, blended color is between them so by averaging the intensity of main colors it would give you teh right blended color according to what you do.

    But if you are going for subtractive color design, just subtract color. Ie primary are basically the reflection of color not absorbed, so two primary color yield teh color that correspond to reflected (not absorbed) color. If you keep it mathematical you could have infinite layer to blend.

    Another way to do subtractive is to start with a white surface and use MUL type of blending.
     
  11. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Unfortunately I did try that but didn't see any change in result. I may try converting this process to use a RenderTexture and Graphics.Blit and see if that is any different (although I assumed GrabPass is essentially the same?).

    @neoshaman
    I don't know enough about shaders or colorspaces to implement those suggestions unfortunately. I feel like what I'm trying to do doesn't warrant a lookup texture. I'm just trying to do what mixing paint would do, while also keeping saturation and value consistent. If each pass has access to the most up-to-date screen texture, the blending should work infinitely, just with lots of overdraw.
     
    Last edited: Jun 19, 2017
  12. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
  13. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    @neoshaman
    All I need to do is sample what's on screen, and average the sprite hue with it like a custom blend operation. I'm already doing that successfully. My problem now is that the GrabPass doesn't seem to have access to the per-pass changes in the screen texture, so it only works for one overlap.
     
  14. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    I think I don't fully understand what you are doing, and why you need multiple pass. You are trying to blend what's on the screen with 3 layer of texture on the same shader?
     
  15. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    In the same way that "Blend One One" will blend everything additively, I'm trying to do a different blend operation where I average the colors.
     
  16. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    I mean I don't see where your different colors are defined in the shader, it seems you could do it in one pass inside the frag. Can you post a pseudo code of teh order of operation you are doing?

    What I understand you want to do:
    1. read a texture once with a shape
    2. use that texture 3 times at different position
    3. give each texture copy a single color
    4. blend each texture color where they overlap but ignore certain pixel
     
  17. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    In the code from my last reply with code, the current background color is from the GrabPass texture, and the new color to blend is coming from _MainTex.

    This is the frag function that does the blending:
    Code (CSharp):
    1. fixed4 frag(v2f input) : SV_Target
    2. {
    3.     float4 textureColor = tex2D(_MainTex, input.uv) * input.color;
    4.     float4 backgroundColor = tex2Dproj(_GrabPass, input.grabPos);
    5.  
    6.     float3 textureColorHSV = rgb2hsv(textureColor);
    7.     float3 backgroundColorHSV = rgb2hsv(backgroundColor);
    8.  
    9.     float3 averaged = (textureColorHSV + backgroundColorHSV) * 0.5f;
    10.  
    11.     float4 result = float4(hsv2rgb(averaged), 1.0);
    12.     return result;
    13. }
    The two passes use the stencil buffer so that the non-overlapping portions are rendered normally, and only the overlaps do this custom blend.
     
  18. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Well I still don't understand why it's so convoluted, but at least there is only one single average here so it cannot blend more than two. But I'm still confuse where you get 3 colors of circle, I'm unable tos ee where in the code you set teh color, basically the color var is set in appdata but I don't see it in parameter so I'm confuse lol, maybe there is something I don't know.

    But I don't see why you would need stencil and pass, it's seems overkill, but since I can't trace where the color come from and there is only one source of texture (grabpass basically duplicate the texture again)

    Code (CSharp):
    1. float4 textureColor1 = tex2D(_MainTex, input.uv)
    2.  
    3. float4 textureColor1 =  float4 textureColor1 * color1;
    4. float4 textureColor2 =  float4 textureColor1 * color2;
    5. float4 textureColor3 =  float4 textureColor1 * color3;
    6.  
    7. //rgb to hsl here
    8.  
    9. float4 averaged = (textureColor1 + textureColor2 + textureColor3) / 3
    10.  
    11. //mask the result with the alpha or any channel
    12.  
    13.  
    14.  
     
  19. vetasoft

    vetasoft

    Joined:
    Nov 15, 2013
    Posts:
    432
    Maybe is this what you want, with Shadero Sprite ( https://www.assetstore.unity3d.com/en/#!/content/97406 )

    you can do it like this
    forum-3.jpg
    I don't know if you want to use it with sprite but you can, in this example, remplace the generated circle with sprite texture.

    Hope it's help!
    Best Regards,
    Vetasoft
     
  20. Deeeds

    Deeeds

    Joined:
    Mar 15, 2018
    Posts:
    739
    How and where to use this to set additive to sprites?
     
  21. aromerocetticsim

    aromerocetticsim

    Joined:
    Nov 27, 2019
    Posts:
    1