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)
using UnityEngine;
using System.Collections;
namespace ImageEffects
{
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class WorldUiBlend : MonoBehaviour
{
[Range(0, 1)]
[SerializeField]
float alpha = 1.0f;
[SerializeField]
RenderTextureFormat renderTextureFormat = RenderTextureFormat.Default;
[SerializeField]
LayerMask mask;
[SerializeField]
Shader shader;
protected Material _material;
Camera worldUiCamera;
RenderTexture renderTexture;
protected virtual void Start()
{
if (!SystemInfo.supportsImageEffects || shader == null || !shader.isSupported)
{
enabled = false;
return;
}
if (CameraExists () && Application.isPlaying)
{
UpdateCamera ();
}
else if (!CameraExists())
{
CreateCamera();
}
}
void MakeNewRenderTexture(int width, int height)
{
if (renderTexture != null)
{
if (Application.isPlaying)
{
Destroy(renderTexture);
}
#if UNITY_EDITOR
else
{
DestroyImmediate(renderTexture);
Resources.UnloadUnusedAssets();
}
#endif
}
renderTexture = new RenderTexture(width, height, 32, renderTextureFormat);
worldUiCamera.targetTexture = renderTexture;
}
void OnDisable()
{
if (renderTexture != null)
{
DestroyImmediate(renderTexture);
}
}
bool CameraExists()
{
bool exist = false;
foreach (Transform child in transform)
{
if (child.gameObject.name == "WorldUiCamera")
{
worldUiCamera = child.gameObject.GetComponent<Camera>();
exist = true;
}
}
return exist;
}
void CreateCamera()
{
var masterCamera = GetComponent<Camera>();
var go = new GameObject("WorldUiCamera", typeof(Camera));
worldUiCamera = go.GetComponent<Camera>();
worldUiCamera.CopyFrom(masterCamera);
worldUiCamera.farClipPlane = 1000;
worldUiCamera.nearClipPlane = 0.19f;
worldUiCamera.cullingMask = mask;
worldUiCamera.hdr = false;
worldUiCamera.depth = masterCamera.depth - 1;
worldUiCamera.clearFlags = CameraClearFlags.SolidColor;
worldUiCamera.backgroundColor = new Color(0, 0, 0, 0);
worldUiCamera.transform.parent = transform;
MakeNewRenderTexture(Screen.width, Screen.height);
}
void UpdateCamera()
{
var masterCamera = this.GetComponent<Camera>();
worldUiCamera.CopyFrom (masterCamera);
worldUiCamera.farClipPlane = 10000;
worldUiCamera.nearClipPlane = 0.19f;
worldUiCamera.cullingMask = mask;
worldUiCamera.hdr = false;
worldUiCamera.depth = masterCamera.depth - 1;
worldUiCamera.clearFlags = CameraClearFlags.SolidColor;
worldUiCamera.backgroundColor = new Color(0, 0, 0, 0);
worldUiCamera.transform.parent = transform;
MakeNewRenderTexture(Screen.width, Screen.height);
}
protected Material Material
{
get
{
if (_material == null)
{
_material = new Material(shader);
_material.hideFlags = HideFlags.HideAndDontSave;
}
return _material;
}
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Material.SetFloat("_Intensity", alpha);
Material.SetTexture("_Overlay", renderTexture);
Graphics.Blit(source, destination, Material);
if (Application.isPlaying && (renderTexture.width != source.width || renderTexture.height != source.height))
{
MakeNewRenderTexture(source.width, source.height);
}
else if (!Application.isPlaying)
{
MakeNewRenderTexture(source.width, source.height);
}
}
}
}
And here is the shader I currently use:
Shader "Hidden/Parabole/ScreenBlend" {
Properties {
_MainTex ("Screen Blended", 2D) = "white" {}
_Overlay ("Overlay", 2D) = "white" {}
_Intensity ("Amount", Range(0.0, 1.0)) = 1.0
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Cull Off
ZWrite Off
ZTest Always
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct appdata_tiny {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
uniform float4 _MainTex_ST;
uniform float4 _Overlay_ST;
v2f vert (appdata_tiny v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
uniform float _Intensity;
uniform sampler2D _MainTex;
uniform sampler2D _Overlay;
fixed4 frag (v2f i) : COLOR
{
half4 tA = tex2D(_MainTex, i.uv);
half4 tB = tex2D(_Overlay, i.uv);
fixed3 result = lerp (tA.rgb, tB.rgb, tB.a * _Intensity);
//fixed3 result = tB.rgb + (tA.rgb * (1 - tB.a));
//fixed3 r2 = (tB.rgb * tB.a) + (tA.rgb * (1 - tB.a));
return fixed4(result, 1);
}
ENDCG
}
}
Fallback off
}
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?