Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Blending two cameras with Image Effects and a RenderTexture

Discussion in 'Shaders' started by alexandre-fiset, Apr 28, 2015.

  1. alexandre-fiset

    alexandre-fiset

    Joined:
    Mar 19, 2012
    Posts:
    699
    Hi!

    I'm trying to figure out a way of having my UI on another camera in order to exclude it from being affected by some image effects while allowing it to be affected by others.The whole thing works, but there are some issues with alpha of some elements "overriding" underlying objects alpha.

    This is how it should look (screenshot from scene view):


    This is how it looks in Game View:


    Here is the script (to be placed on the Main Camera)
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. namespace ImageEffects
    5. {
    6.     [ExecuteInEditMode]
    7.     [RequireComponent(typeof(Camera))]
    8.     public class WorldUiBlend : MonoBehaviour
    9.     {
    10.         [Range(0, 1)]
    11.         [SerializeField]
    12.         float alpha = 1.0f;
    13.         [SerializeField]
    14.         RenderTextureFormat renderTextureFormat = RenderTextureFormat.Default;
    15.         [SerializeField]
    16.         LayerMask mask;
    17.         [SerializeField]
    18.         Shader shader;
    19.         protected Material _material;
    20.         Camera worldUiCamera;
    21.         RenderTexture renderTexture;
    22.  
    23.         protected virtual void Start()
    24.         {
    25.             if (!SystemInfo.supportsImageEffects || shader == null || !shader.isSupported)
    26.             {
    27.                 enabled = false;
    28.                 return;
    29.             }
    30.  
    31.             if (CameraExists () && Application.isPlaying)
    32.             {
    33.                 UpdateCamera ();
    34.             }
    35.  
    36.             else if (!CameraExists())
    37.             {
    38.                 CreateCamera();
    39.             }
    40.        
    41.         }
    42.  
    43.         void MakeNewRenderTexture(int width, int height)
    44.         {
    45.             if (renderTexture != null)
    46.             {
    47.                 if (Application.isPlaying)
    48.                 {
    49.                     Destroy(renderTexture);
    50.                 }
    51.  
    52.                 #if UNITY_EDITOR
    53.                 else
    54.                 {
    55.                     DestroyImmediate(renderTexture);
    56.                     Resources.UnloadUnusedAssets();
    57.                 }
    58.                 #endif
    59.             }
    60.  
    61.          
    62.             renderTexture = new RenderTexture(width, height, 32, renderTextureFormat);
    63.            
    64.             worldUiCamera.targetTexture = renderTexture;
    65.         }
    66.  
    67.         void OnDisable()
    68.         {
    69.             if (renderTexture != null)
    70.             {
    71.                 DestroyImmediate(renderTexture);
    72.             }
    73.         }
    74.  
    75.         bool CameraExists()
    76.         {
    77.  
    78.             bool exist = false;
    79.             foreach (Transform child in transform)
    80.             {
    81.                 if (child.gameObject.name == "WorldUiCamera")
    82.                 {
    83.                     worldUiCamera = child.gameObject.GetComponent<Camera>();
    84.                     exist = true;
    85.                 }
    86.             }
    87.  
    88.             return exist;
    89.         }
    90.  
    91.         void CreateCamera()
    92.         {
    93.             var masterCamera = GetComponent<Camera>();
    94.             var go = new GameObject("WorldUiCamera", typeof(Camera));
    95.             worldUiCamera = go.GetComponent<Camera>();
    96.             worldUiCamera.CopyFrom(masterCamera);
    97.             worldUiCamera.farClipPlane = 1000;
    98.             worldUiCamera.nearClipPlane = 0.19f;
    99.             worldUiCamera.cullingMask = mask;
    100.             worldUiCamera.hdr = false;
    101.             worldUiCamera.depth = masterCamera.depth - 1;
    102.             worldUiCamera.clearFlags = CameraClearFlags.SolidColor;
    103.             worldUiCamera.backgroundColor = new Color(0, 0, 0, 0);
    104.             worldUiCamera.transform.parent = transform;
    105.  
    106.             MakeNewRenderTexture(Screen.width, Screen.height);
    107.         }
    108.  
    109.         void UpdateCamera()
    110.         {
    111.             var masterCamera = this.GetComponent<Camera>();
    112.  
    113.             worldUiCamera.CopyFrom (masterCamera);
    114.             worldUiCamera.farClipPlane = 10000;
    115.             worldUiCamera.nearClipPlane = 0.19f;
    116.             worldUiCamera.cullingMask = mask;
    117.             worldUiCamera.hdr = false;
    118.             worldUiCamera.depth = masterCamera.depth - 1;
    119.             worldUiCamera.clearFlags = CameraClearFlags.SolidColor;
    120.             worldUiCamera.backgroundColor = new Color(0, 0, 0, 0);
    121.             worldUiCamera.transform.parent = transform;
    122.            
    123.             MakeNewRenderTexture(Screen.width, Screen.height);
    124.         }
    125.  
    126.         protected Material Material
    127.         {
    128.             get
    129.             {
    130.                 if (_material == null)
    131.                 {
    132.                     _material = new Material(shader);
    133.                     _material.hideFlags = HideFlags.HideAndDontSave;
    134.                 }
    135.  
    136.                 return _material;
    137.             }
    138.         }
    139.  
    140.  
    141.  
    142.         void OnRenderImage(RenderTexture source, RenderTexture destination)
    143.         {
    144.             Material.SetFloat("_Intensity", alpha);
    145.             Material.SetTexture("_Overlay", renderTexture);
    146.             Graphics.Blit(source, destination, Material);
    147.  
    148.             if (Application.isPlaying && (renderTexture.width != source.width || renderTexture.height != source.height))
    149.             {
    150.                 MakeNewRenderTexture(source.width, source.height);
    151.             }
    152.  
    153.             else if (!Application.isPlaying)
    154.             {
    155.                 MakeNewRenderTexture(source.width, source.height);
    156.             }
    157.  
    158.  
    159.         }
    160.  
    161.     }
    162. }
    And here is the shader I currently use:
    Code (CSharp):
    1. Shader "Hidden/Parabole/ScreenBlend" {
    2.     Properties {
    3.         _MainTex ("Screen Blended", 2D) = "white" {}
    4.         _Overlay ("Overlay", 2D) = "white" {}
    5.         _Intensity ("Amount", Range(0.0, 1.0)) = 1.0
    6.     }
    7.    
    8. SubShader {
    9.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    10.         Cull Off
    11.         ZWrite Off
    12.         ZTest Always
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.  
    15.         Pass {
    16.             CGPROGRAM
    17.                 #pragma vertex vert
    18.                 #pragma fragment frag
    19.                 #pragma fragmentoption ARB_fog_exp2
    20.                 #pragma fragmentoption ARB_precision_hint_fastest
    21.                 #include "UnityCG.cginc"
    22.              
    23.                 struct appdata_tiny {
    24.                     float4 vertex : POSITION;
    25.                     float4 texcoord : TEXCOORD0;
    26.                 };
    27.              
    28.                 struct v2f {
    29.                     float4 pos : SV_POSITION;
    30.                     float2 uv : TEXCOORD0;
    31.                 };
    32.              
    33.                 uniform float4  _MainTex_ST;
    34.                 uniform float4 _Overlay_ST;
    35.              
    36.                 v2f vert (appdata_tiny v)
    37.                 {
    38.                     v2f o;
    39.                     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    40.                     o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
    41.                     return o;
    42.                 }
    43.              
    44.                 uniform float _Intensity;
    45.                 uniform sampler2D  _MainTex;
    46.                 uniform sampler2D  _Overlay;
    47.              
    48.                 fixed4 frag (v2f i) : COLOR
    49.                 {
    50.                     half4   tA = tex2D(_MainTex, i.uv);
    51.                     half4   tB = tex2D(_Overlay, i.uv);
    52.                    
    53.                     fixed3 result  = lerp (tA.rgb, tB.rgb, tB.a * _Intensity);
    54.                     //fixed3 result =  tB.rgb + (tA.rgb * (1 - tB.a));
    55.                     //fixed3 r2 = (tB.rgb * tB.a) + (tA.rgb * (1 - tB.a));
    56.                     return fixed4(result, 1);
    57.                 }
    58.             ENDCG
    59.         }
    60.         }
    61.  
    62. Fallback off
    63.    
    64. }
    And for those interested, attached is a simple scene with another shader variant and the whole problem exposed.

    I have tried many alpha blending methods and each of them has the "alpha overriding" problem. As soon as something is transparent, everything behind it becomes transparent.

    Does anybody know what the problem could be?
     

    Attached Files:

  2. Zicandar

    Zicandar

    Joined:
    Feb 10, 2014
    Posts:
    388
    (After I try to give a potential solution like what your asking for I'll give a suggestion on a different way of doing it :)
    A guess as to why this happens:
    The alpha channels are blended the same way the colors are blended.
    Try using this as blend mode for the GUI shaders. (Not NOT the merge shader!)
    (I'm having some trouble opening your package, most likely due to being at 5.0.0 and not 5.0.1, but upgrading as I write.
    Try using
    Blend SrcAlpha OneMinusSrcAlpha, SrcAlpha One
    as blend mode for transparent stuff.
     
  3. Zicandar

    Zicandar

    Joined:
    Feb 10, 2014
    Posts:
    388
    Then my suggestion:
    Use 2 cameras instead!
    The second one you set to clear depth only.
    And any post process you want done after both GUI AND normal stuff is done you put in there.
    And I got your project to open, sadly I'm not sure if that blend mode fix I suggested can be used without overriding the sprite shader. Testing if I can do that now. But still, 2 cameras seems more reasonable.
    Now I got that to work, it won't look IDENTICAL because of how blending works. (Sequential blending is dependant on the undermost color, so my solution is in theory more correct.)
    The problems with my solution:
    With MSAA ON, unity has a tendancy to flip the screen updside down. So some post process you have is most likely missing the y flip define stuff that all post processes seem to need...
     

    Attached Files:

  4. alexandre-fiset

    alexandre-fiset

    Joined:
    Mar 19, 2012
    Posts:
    699
    Well I now feel like a total noob for not trying the two cameras setup in my test scene. That causes issues in my real project where I'm using HDR. Combining two cameras with image effects make the fog and a lot of other things disappear.

    I think we can close this thread, I'll pinpoint the HDR issue and will start a new one.

    Thanks Zicandar :)
     
  5. alexandre-fiset

    alexandre-fiset

    Joined:
    Mar 19, 2012
    Posts:
    699