3
\$\begingroup\$

I want to use "Screen blending" from Photoshop in my Unity 2D game, I want to use that for exported lights from 3D Max that we want to use in our game, Now for a sample, I have my sprite that I want to mask it :

sprite that I want to mask it

And I also have a mask for it :

mask

Now my question is how to make this in Unity like this :

enter image description here

Can make this happen by Sprite mask?

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

My original answer to this (check the edit history if you're curious) said that this couldn't be done in a single pass due to the way GPUs' semi-programmable blending stage works, and offered a more complicated work-around with multiple drawing passes, as well as similar-looking alternatives with non-screen blend modes.

But apparently I overlooked the fact that the formula for screen blending can be rearranged into a format that fits the GPU just fine!

$$\begin{align} & 1 - (1 - \text{source}) \cdot (1 - \text{destination})\\ =& 1 + (\text{source} - 1) \cdot (1 - \text{destination})\\ =& 1 + (\text{source} - \text{source} \cdot \text{destination} - 1 + \text{destination} )\\ =& \text{source} - \text{source} \cdot \text{destination} + \text{destination}\\ =& \text{source} \cdot (1 -\text{destination}) + \text{destination}\\ \end{align}$$

So, we can write this in Unity ShaderLab as:

Blend OneMinusDstColor One

(OneMinusDstColor is the \$(1 -\text{destination})\$ term, implicitly multiplied by the source colour since it's in the first slot. The One gets multiplied by the destination colour since it's in the second slot, and the two results get added together)

So with that, here's a full Unity shader that implements screen blending in one pass, without my previous over-engineering:

Shader "Unlit/ScreenBlend"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        LOD 100

        Blend OneMinusDstColor One
        Zwrite Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                
                col.rgb *= col.a;
                return col;
            }
            ENDCG
        }
    }
}

(I've shown this applied to a basic Unlit shader, but you can make a copy of the standard Unity Sprite shader and replace the blend formula with the one above to achieve the same effect, while also getting all the features you're used to with sprites)


In the original answer, I also shared a few other options, compared with OP's images here:

Jellyfish four ways

  • On the left is a correct Screen blend mode

  • Second from the left is standard alpha/layer blending, using the flare image from the question as a greyscale texture, with alpha from greyscale.

    Note the slight dark fringing around the edges: these are spots where the white of the flare blends partway to black in the colour channels, so we blend the resulting grey onto the background.

    What we have here is effectively a pre-multiplied alpha image being blended with a non-premultiplied-alpha shader. You could fix this by making the shader use a correct pre-multiplied blend function (Blend One OneMinusSrcAlpha), or...

  • The next image shows a way to correct this while using standard blending. I've used the lens flare image as the alpha channel of an image, and filled the colour channels with pure white. This way, we're always blending pure white with varying degrees of transparency, rather than darker greys.

    You can see the falloff isn't too different than the Screen blending, it's just a bit brighter overall, so applying a levels or curves adjustment to the alpha channel might be all you need for a screen-like effect in this case, with no custom shader.

  • Lastly, on the right, is additive blending. This tends to look quite good for light/glow effects, though it does exaggerate colours as you can see here in the bright blue halo it gets over a blue background.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for your amazing answer! I've used your second solution by creating a Sprite(2D and UI) texture type from my flare image and changing the alpha source to "From Gray Scale". but in your third solution, you said: "I've used the lens flare image you shared as the alpha channel of an image", and I have no idea how can do that, can you please explain a little more? Thanks again! \$\endgroup\$ Commented Aug 7, 2018 at 8:11
  • \$\begingroup\$ For the third case, create a new image of the same size in Photoshop. Fill the background layer with white. Then go to the Channels window and add a new Alpha channel. Paste your flare image into the new channel, and save the result as a TARGA (TGA) \$\endgroup\$
    – DMGregory
    Commented Aug 7, 2018 at 11:07
  • \$\begingroup\$ Thanks now it works! , To use the last solution, I have to say I never worked with shaders before, but I will dive into it and let you know about the result :) \$\endgroup\$ Commented Aug 7, 2018 at 17:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .