Search Unity

Image effects on Mobile

Discussion in 'Image Effects' started by wtetotew, Jan 27, 2021.

  1. wtetotew

    wtetotew

    Joined:
    Apr 12, 2020
    Posts:
    68
    Hello,

    Does any experimented user know what is the best way to achieve simple image effects for a mobile game?


    Currently I'm using a white square sprite with a custom shader like this:
    Code (CSharp):
    1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    2.  
    3. Shader "Custom/UI/Invert"
    4. {
    5.     Properties
    6.     {
    7.         [PerRendererData] _MainTex ("Sprite Texture (A)", 2D) = "white" {}
    8.         _Color ("Tint", Color) = (1,1,1,1)
    9.  
    10.          _StencilComp("Stencil Comparison", Float) = 8
    11.          _Stencil("Stencil ID", Float) = 0
    12.          _StencilOp("Stencil Operation", Float) = 0
    13.          _StencilWriteMask("Stencil Write Mask", Float) = 255
    14.          _StencilReadMask("Stencil Read Mask", Float) = 255
    15.          _ColorMask ("Color Mask", Float) = 15
    16.     }
    17.  
    18.     SubShader
    19.     {
    20.         Tags
    21.         {
    22.             "Queue"="Transparent"
    23.             "IgnoreProjector"="True"
    24.             "RenderType"="Transparent"
    25.             "PreviewType"="Plane"
    26.             "CanUseSpriteAtlas"="True"
    27.         }
    28.        
    29.         Stencil
    30.         {
    31.             Ref [_Stencil]
    32.             Comp [_StencilComp]
    33.             Pass [_StencilOp]
    34.             ReadMask [_StencilReadMask]
    35.             WriteMask [_StencilWriteMask]
    36.         }
    37.  
    38.         Cull Off
    39.         Lighting Off
    40.         ZWrite Off
    41.         ZTest [unity_GUIZTestMode]
    42.         Fog { Mode Off }
    43.         ColorMask [_ColorMask]
    44.  
    45.         // invert color blend mode
    46.         Blend OneMinusDstColor OneMinusSrcAlpha
    47.         BlendOp Add
    48.  
    49.  
    50.         Pass
    51.         {
    52.         CGPROGRAM
    53.             #pragma vertex vert
    54.             #pragma fragment frag
    55.             #include "UnityCG.cginc"
    56.            
    57.             struct appdata_t
    58.             {
    59.                 float4 vertex   : POSITION;
    60.                 fixed4 color    : COLOR;
    61.                 half2 texcoord : TEXCOORD0;
    62.             };
    63.  
    64.             struct v2f
    65.             {
    66.                 float4 vertex   : SV_POSITION;
    67.                 fixed4 color    : COLOR;
    68.                 half2 texcoord  : TEXCOORD0;
    69.             };
    70.            
    71.             fixed4 _Color;
    72.             sampler2D _MainTex;
    73.  
    74.             v2f vert(appdata_t IN)
    75.             {
    76.                 v2f OUT;
    77.                 OUT.vertex = UnityObjectToClipPos(IN.vertex);
    78.                 OUT.texcoord = IN.texcoord;
    79.                 OUT.color = IN.color * _Color;
    80.                 return OUT;
    81.             }
    82.  
    83.             fixed4 frag(v2f IN) : SV_Target
    84.             {
    85.                 fixed4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
    86.                 return color;
    87.             }
    88.         ENDCG
    89.         }
    90.     }
    91. }
    92.  


    This one is for Inverting color( I haven't succeed to make a Greyscale BTW if someone can help).

    This method is the best for performance on mobile right?
     
  2. wtetotew

    wtetotew

    Joined:
    Apr 12, 2020
    Posts:
    68
    Anyone from unity have an opinion on this?
     
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I'm interested in this too. My need is actually to convert the image to grayscale on Quest, and obviously it needs to do so without a huge drag in performance.
     
  4. wtetotew

    wtetotew

    Joined:
    Apr 12, 2020
    Posts:
    68
    Me it's only for 2D but still cannot know what is the more performant way for mobile.

    I'm a bit surprised that this is not more discussed.

    Do people just don't use any image effects when they build for mobile?
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I found a hint of a suggestion here: https://forums.oculusvr.com/develop...ocessing-or-fullscreen-image-effects-on-quest

    Still looking for a code example of this approach.
     
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    OK, the following mostly works (applied to a quad placed in front of the camera). But it doesn't affect the skybox. Still need to figure that out.

    Code (CSharp):
    1. Shader "Color FX/Green"
    2. {
    3.     Properties
    4.     {
    5. //        _Color ("Color", Color) = (1,1,1,1)
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Overlay" "PreviewType"="Plane" }
    10.         Pass {
    11.             Zwrite Off
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             // only compile Shader for platforms that can potentially
    16.             // do it (currently gles,gles3,metal)
    17.             #pragma only_renderers framebufferfetch
    18.          
    19.             struct appdata {
    20.                 float4 vertex : POSITION;
    21.                 float2 uv : TEXCOORD0;
    22.             };
    23.          
    24.             struct v2f {
    25.                 float4 vertex : SV_POSITION;
    26.             };
    27.          
    28.             v2f vert (appdata v) {
    29.                 v2f o;
    30.                 o.vertex = UnityObjectToClipPos(v.vertex);
    31.                 return o;
    32.             }
    33.          
    34.             void frag (v2f v, inout half4 ocol : SV_Target)
    35.             {
    36.                 // ocol can be read (current framebuffer color)
    37.                 // and written into (will change color to that one)
    38.                 ocol.g = (ocol.r + ocol.g + ocol.b) * 0.33f;
    39.                 ocol.r = ocol.b = 0;
    40.             }
    41.             ENDCG
    42.         }
    43.     }
    44.     Fallback Off
    45. }
     
    Last edited: Feb 1, 2021
  7. wtetotew

    wtetotew

    Joined:
    Apr 12, 2020
    Posts:
    68
    Hi! Interesting. What kind of shader do you create to make it work?
    Do you then create a material that you put on the quad?

    Still, it would be very helpfull to have a answer from Unity about this topic.
    Is it better to avoid completely image effect on mobile CPU?

    Even if the answer is yes, it's better to have a clear answer about this.
     
  8. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Yes, you put this on a quad in front of the camera. It's working great for me (on Oculus Quest).
     
  9. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Unity's explained this many times in it's documentation, on the blog and so on. You are being a bit lazy. I don't mind that, as it's a smart way to get good information.

    However in this topic, it's been beaten to death. The forums are stuffed with mobile post fx advice as is the asset store.

    For Quest you want to just modify the shaders, this will be fastest. You don't need a whole full screen blit if you're changing the colour of all the meshes being rendered.
    Code (CSharp):
    1. float4 colour = tex2D(_colour, i.uv);
    2. colour.rgb = lerp(Luminance(colour.rgb), colour.rgb, _colourSaturation);
    _colourSaturation would be a number you pass into the shader between 0 and 1.
    Code (CSharp):
    1. inline float Luminance(float3 c)
    2. {
    3.     return dot(c, float3(0.22, 0.707, 0.071));
    4. }
    The Luminance function is extremely fast.

    This method would be essentially close to zero cost on most hardware, while allowing you to choose how much saturation you have (you can overboost it going past with _colourSaturation or greyscale if 0).

    So basically modify the original shader if you care about performance, or you pay a fairly heavy price since you would have to ask URP (or builtin) to provide a Color buffer, which involves blitting the backbuffer to a texture, then blitting that texture with a shader. This is how all post fx is done usually.

    Some effects can't be done in-shader, and if you are using shader graph then lighting will still colour the image as there is no access to the final pixel colour from graphs at the time of writing.
     
  10. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Well, modifying every shader that might be on screen is great if you can manage it. In my case that feels impractical — we have terrains rendered with MicroSplat, a dynamic sky with its own shaders, UI (including TextMeshPro) of course doing its own thing, and then the half-dozen different shaders we use for various purposes.

    The framebuffer fetch approach seems to work great though (and I was able to get it to affect the skybox, too, after correcting the rendering queue), and it doesn't require touching everything else that renders to the screen. And for desktop (or other platforms that don't support framebuffer fetch), I just made a second subshader that uses GrabPass. Life is good.
     
    hippocoder likes this.
  11. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    How does Microsplat hold up on Quest, many addons?
     
  12. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Seems to do fine in its minimal configuration. The docs include some helpful notes on performance optimization.

    We're not using any add-ons except procedural texturing, and even that we will probably bake (thus becoming non-procedural) when we get a little further along.
     
    hippocoder likes this.