Invert the colors of the Camera

Hi,
I’ve been dealing with this problem for many days.

I want to invert the colors of all my scene in my Android Game.
I tried using post-processing efects, but I didn’t see any of them that could invert the colors.

I also tried to use shaders, although I don’t know much about them.
I applied a shader to the Camera (I copied and pasted the code of Unity Standard Assets → Image Effects, I founded a shader that inverts colors and I pasted there).
Now in the editor and in some android devices (those who have a lot of cpu and gpu) works like a charm.

The problem is that in low-quality android devices it all starts doing strange things and the game seems almost blocked when I active that shader.

How should I do? I don’t know much about shaders.
Thanks:)

The default image effect shader that Unity creates when you create a new image effect shader in the project view is actually a color inversion shader. It’s a super simple effect.

The problem with old android devices is the way Unity handles image effects is to copy the frame buffer to a render texture and pass that to the image effect shader. This is pretty fast on most hardware, but very very slow on older hardware. The usual fix people will suggest is to not use Unity’s built in OnRenderImage and instead have your camera render directly to a render texture and do everything with command buffers and manually swapping the render target.

However for the effect of flipping the color there’s an easy way that doesn’t require using image effects, or render textures, or any of this. All that’s needed is a quad in front of the camera with a very simple shader.

Shader "Custom/InvertColor"
{
    SubShader
    {
        Pass
        {
            Tags { "Queue"="Overlay+100" "IgnoreProjector"="True" }
  
            Blend OneMinusDstColor Zero
            ColorMask RGB
  
            ZWrite Off
            ZTest Always

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
  
            float4 vert(float4 vertex : POSITION) : SV_Position {
              return UnityObjectToClipPos(vertex);
            }

            fixed4 frag() : SV_Target {
              return fixed4(1,1,1,1);
            }

            ENDCG
        }
    }
}

The key part is that Blend line. Basically the rest of the shader does the absolute minimum work possible to render the mesh to the screen, and the blend mode is what tells it to render the current screen color inverted.

edit: Fixed shader so it compiles

2 Likes

Hi,

Thanks for your answer, the shader you did works perfectly.
I did what you said, a quad with your shader. I tried it in low-quality mobiles and it works! Thank you very much!

I have a last question, I need to change the colour from normal to inverted in a transition, like if normal was 0 and inverted color was 1, what I would do is change the value from 0 to 0.25, then 0.5, etc.

Thank you so much, this was the last thing I needed to finish my game.

bgolus’ shader can’t change the inversion level, but this can be fixed:

    Shader "Custom/InvertColor"
    Properties {
        _TintColor ("Tint Color", Color) = (1,1,1,1)
    }
    {
        SubShader
        {
            Pass
            {
                Tags { "Queue"="Overlay+100" "IgnoreProjector"="True" }
    
                Blend OneMinusDstColor OneMinusSrcColor
                ColorMask RGB
    
                ZWrite Off
                ZTest Always
    
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                float4 vert(float4 vertex : POSITION) : SV_Position {
                  return UnityObjectToClipPos(vertex);
                }
    
                fixed4 frag() : SV_Target {
                  return fixed4(1,1,1,1);
                }
    
                ENDCG
            }
        }
    }

Then you change the Tint Color between black and white (or other color like red for actually tinting the result). In script you SetColor to _TintColor and apply lerp to RGB (alpha is irrelevant).

3 Likes

It works fine, like bgolus’s one.
But when I try to set the colour like you said, nothing changes.

material.SetColor("_TintColor", Color.Lerp(Color.white, Color.black, 1f));
//and then the same but from  black to white
material.SetColor("_TintColor", Color.Lerp(Color.black, Color.white, 1f));

If I change the color manually in the inspector, nothing changes either.
The texture is black and remains black.

I think I should learn how to code shaders:face_with_spiral_eyes:
Thanks for everything!

I believe @ifurkend forgot to update the shader fully. He defined the _TintColor property and modified the Blend mode, but never actually makes use of the new property. I suspect this is what he had intended to post.

Shader "Custom/InvertColor"
{
    Properties {
        _TintColor ("Tint Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags { "Queue"="Overlay+100" "IgnoreProjector"="True" }
   
            Blend OneMinusDstColor OneMinusSrcColor
            ColorMask RGB
   
            ZWrite Off
            ZTest Always

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
   
            float4 vert(float4 vertex : POSITION) : SV_Position {
              return UnityObjectToClipPos(vertex);
            }

            fixed4 _TintColor;

            fixed4 frag() : SV_Target {
              return _TintColor;
            }

            ENDCG
        }
    }
}
4 Likes

Thank you very much for your help!!
Now the shader works perfectly, and in low-quality mobiles it does too!

I can finally end my game.
Many thanks to both for your help!!:):smile:

@bgolus : Yes. Because I already have my invert color shader, I forgot to compile the code before posting… orz

I realize this thread is a couple of years old, but is there a way to “cut a hole” in the inverted shader above using a texture with transparency?

Here’s what I’ve got:

Shader "Custom/InvertColor1"
{
    Properties{
        _TintColor("Tint Color", Color) = (1,1,1,1)
        //_MainTex("Base (RGB)", 2D) = "white" {} TODO
    }
        SubShader
    {
        Pass
        {
            Tags { "Queue" = "Overlay+100" "IgnoreProjector" = "True" }

            Blend OneMinusDstColor OneMinusSrcColor
            ColorMask RGB

            ZWrite Off
            ZTest Always

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            float4 vert(float4 vertex : POSITION) : SV_Position {
              return UnityObjectToClipPos(vertex);
            }

            fixed4 _TintColor;

            fixed4 frag() : SV_Target {
              return _TintColor;
            }

            ENDCG
        }
    }
}