I'm trying to achieve an effect where I have a completely transparent object (so totally invisible, as seen using Fade or Cutout, rather than Transparent or Opaque), and anything behind that object is rendered differently to those out in the open, similar to this: (but with a transparent cube at the front - and what I really want is to change the alpha of the objects at the back when they are behind the invisible cube). The bunnies have a script on them to change how they look when they are behind the object with the shader. The shader looks like this: Code (CSharp): Shader "Occlusion FX/Occlusion Mask" { Properties { _MainTex ("Main", 2D) = "white" {} _Glossiness ("Smoothness", Range(0, 1)) = 0 _Metallic ("Metallic", Range(0, 1)) = 0 } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry-1" } Stencil { Ref 128 Comp Always Pass Replace } CGPROGRAM #pragma surface surf Standard #include "Utils.cginc" ENDCG } FallBack Off } Here is what Utils.cginc looks like: Code (CSharp): #ifndef UTILS_INCLUDED #define UTILS_INCLUDED sampler2D _MainTex; half _Metallic, _Glossiness; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutputStandard o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; o.Metallic = _Metallic; o.Smoothness = _Glossiness; } #endif The problem is that the cube at the front is not totally invisible, even though I have assigned it a transparent texture. I have another Shader which succeeds in completely hiding the front cube by adding ZWrite Off and ZTest Greater to the SubShader like this: Code (CSharp): Shader "Occlusion FX/Occlusion Glow Source" { Properties {} SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry" } ZTest Greater ZWrite Off CGPROGRAM #pragma surface surf Unlit struct Input { float2 uv_MainTex; float3 viewDir; }; void surf (Input IN, inout SurfaceOutput o) { half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal)); rim = pow(rim, 1.0); o.Albedo = fixed3(1, 1, 1) * rim; o.Alpha = 1.0; o.Emission = fixed3(0.2, 0.2, 0.2); } inline fixed4 LightingUnlit (SurfaceOutput s, fixed3 lightDir, fixed atten) { return fixed4(s.Albedo, s.Alpha); } ENDCG } FallBack Off } but then the bunnies at the back never pick up the effects. If i use a standard shader which I make invisible using Fade or Cutout, there is no effect on the bunnies either. I read this post by @bgolus https://forum.unity.com/threads/render-mode-transparent-doesnt-work-see-video.357853/#post-2315934 but it seems like my 2nd shader is turning ZWrite Off to make the cube invisible, but I understood his post to say I should be adding: Pass { ZWrite On ColorMask 0 } if I use this shader he linked to, it completely hides the bunnies when they are behind the box (though it does succeed in making the box totally invisible): Code (CSharp): Shader "Transparent/Diffuse ZWrite" { Properties { _Color ("Main Color", Color) = (1,1,1,1) _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {} } SubShader { Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} LOD 200 // extra pass that renders to depth buffer only Pass { ZWrite On ColorMask 0 } // paste in forward rendering passes from Transparent/Diffuse UsePass "Transparent/Diffuse/FORWARD" } Fallback "Transparent/VertexLit" } Obviously I don't really know anything about shaders, but I'm hoping someone can point me in the right direction to achieve this effect. For completeness, here is the script on the bunnies (though I dont think it is important): Code (CSharp): using UnityEngine; public class OcclusionFX : MonoBehaviour { public enum EFX { EFX_Translucency, EFX_Pattern, EFX_Wireframe, EFX_XRay, EFX_Stripe, EFX_Outline }; public EFX m_fx = EFX.EFX_XRay; private EFX m_currFx = EFX.EFX_XRay; [Header("Translucency")] [Range(0.1f, 0.5f)] public float m_Alpha = 0.3f; public Color m_Tint = Color.white; public float m_TintIntensity = 1f; [Header("Pattern")] public Texture2D m_PatternTex; public float m_PatternScale = 64; public Color m_PatternColor = Color.yellow; [Header("Wireframe")] [Range(1f, 2000f)] public float m_WireThickness = 800f; [Range(0f, 1f)] public float m_WireClipThreshold = 0.5f; public Color m_WireColor = Color.green; [Header("XRay")] public Color m_EmissionColor = Color.white; public Color m_XrayColor = Color.green; [Range(0.01f, 2f)] public float m_XrayPower = 0.1f; [Header("Stripe")] public Texture2D m_StripeTex; public Color m_StripeColor = new Color (1f, 0.8f, 0f, 1f); [Range(0.05f, 0.5f)] public float m_StripeWidth = 0.1f; [Range(0.1f, 10f)] public float m_StripeDensity = 5f; [Header("Outline")] [Range(0.01f, 0.1f)] public float m_OutlineWidth = 0.03f; public Color m_OutlineColor = new Color (0f, 1f, 0f, 1f); Shader m_SdrTranslucency; Shader m_SdrPattern; Shader m_SdrWireframe; Shader m_SdrXRay; Shader m_SdrStripe; Shader m_SdrOutline; Renderer m_Renderer; Material[] GetMaterials () { if (Application.isPlaying) return m_Renderer.materials; else return m_Renderer.sharedMaterials; } void SetMaterialsFloat (string name, float f) { Material[] mats = GetMaterials (); for (int i = 0; i < mats.Length; i++) mats[i].SetFloat (name, f); } void SetMaterialsColor (string name, Color c) { Material[] mats = GetMaterials (); for (int i = 0; i < mats.Length; i++) mats[i].SetColor (name, c); } void SetMaterialsTexture (string name, Texture t) { Material[] mats = GetMaterials (); for (int i = 0; i < mats.Length; i++) mats[i].SetTexture (name, t); } void SetMaterialsShader (Shader sdr) { Material[] mats = GetMaterials (); for (int i = 0; i < mats.Length; i++) mats[i].shader = sdr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Start () { m_Renderer = GetComponent<Renderer> (); m_SdrTranslucency = Shader.Find ("Occlusion FX/Translucency"); m_SdrPattern = Shader.Find ("Occlusion FX/Pattern"); m_SdrWireframe = Shader.Find ("Occlusion FX/Wireframe"); m_SdrXRay = Shader.Find ("Occlusion FX/XRay"); m_SdrStripe = Shader.Find ("Occlusion FX/Stripe"); m_SdrOutline = Shader.Find ("Occlusion FX/Outline"); ChangeFXShader (); UpdateFXParameters (); } void Update () { UpdateFXParameters (); if (m_currFx != m_fx) { m_currFx = m_fx; ChangeFXShader (); } } void UpdateFXParameters () { if (m_currFx == EFX.EFX_Translucency) { SetMaterialsFloat ("_Alpha", m_Alpha); SetMaterialsColor ("_Tint", m_Tint); SetMaterialsFloat ("_TintIntensity", m_TintIntensity); } else if (m_currFx == EFX.EFX_Pattern) { SetMaterialsTexture ("_PatternTex", m_PatternTex); SetMaterialsFloat ("_PatternScale", m_PatternScale); SetMaterialsColor ("_PatternColor", m_PatternColor); } else if (m_currFx == EFX.EFX_Wireframe) { SetMaterialsFloat ("_WireThickness", m_WireThickness); SetMaterialsFloat ("_ClipThreshold", m_WireClipThreshold); SetMaterialsColor ("_WireColor", m_WireColor); } else if (m_currFx == EFX.EFX_XRay) { SetMaterialsFloat ("_XrayPower", m_XrayPower); SetMaterialsColor ("_EmissionColor", m_EmissionColor); SetMaterialsColor ("_XrayColor", m_XrayColor); } else if (m_currFx == EFX.EFX_Stripe) { SetMaterialsFloat ("_StripeWidth", m_StripeWidth); SetMaterialsFloat ("_StripeDensity", m_StripeDensity); SetMaterialsColor ("_StripeColor", m_StripeColor); SetMaterialsTexture ("_StripeTex", m_StripeTex); } else if (m_currFx == EFX.EFX_Outline) { SetMaterialsColor ("_OutlineColor", m_OutlineColor); SetMaterialsFloat ("_Outline", m_OutlineWidth); } } void ChangeFXShader () { if (m_currFx == EFX.EFX_Translucency) SetMaterialsShader (m_SdrTranslucency); else if (m_currFx == EFX.EFX_Pattern) SetMaterialsShader (m_SdrPattern); else if (m_currFx == EFX.EFX_Wireframe) SetMaterialsShader (m_SdrWireframe); else if (m_currFx == EFX.EFX_XRay) SetMaterialsShader (m_SdrXRay); else if (m_currFx == EFX.EFX_Stripe) SetMaterialsShader (m_SdrStripe); else if (m_currFx == EFX.EFX_Outline) SetMaterialsShader (m_SdrOutline); } }
Because your shader is an opaque shader. You never set the surface shader to use “alpha” in the #pragma surface line, so by default it’ll completely ignore whatever o.Alpha is set to and won’t do any kind of blend or fade. That’s not the real problem though. The real problem is your mask shader should not be a surface shader. Neither should your glow shader, or really any shader that you don’t want to interact with Unity’s lighting system. Surface shaders are vertex fragment shader generators explicitly designed to let users use Unity’s lighting systems easily. If you don’t want lighting, use a vertex fragment shader. Even if you use the hack “Unlit” custom lighting function with a Surface Shader, it will still have weird problems when you have multiple lights in the scene and you're paying some additional unnecessary costs as the shader is doing more work than you need. The "Diffuse ZWrite" shader you posted is closer to what you want, but you explicitly do not want the UsePass or the Fallback as you really just want a shader that does as little as possible. Code (CSharp): Shader "Occlusion FX/Occlusion Mask" { SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry-1" } Stencil { Ref 128 Comp Always Pass Replace } Pass { Fog { Mode Off } Color (0,0,0,0) ColorMask 0 } } } Note that the Pass {} with no CGPROGRAM block inside is also vertex fragment shader generator. This used to be for fixed function shaders, but those no longer really exist, so instead that generates a simple vertex fragment shader. The above code will construct a very simple shader, though curiously a hand written one would still be slightly faster. The main issue you’re having is you’re not thinking about the rendering order. Be it stencil based or depth buffer based, the "mask" has to render before the objects you want to be hidden / shown are rendered. A combination of stencil and ZWrite is likely what you'll need in the end. There are a number of threads on the forum for how to go about this. It all depends on exactly how you want this to behave. For example: The invisible cube mask, is it only hiding the bunnies or specific objects in the scene, or everything in the scene? Is the mask hiding everything that appears in that part of the screen, or only things that are behind it; ie: if a bunny is closer to the camera than the box does it render normally? If there's an object in front of the mask, do the "glows" show on top of that object too, or should it only show where the cube is "visible". This thread (and the one linked to within it) go into these topics. https://forum.unity.com/threads/sorting-issues-with-stencil-masks.526740/
Actually the script you provided does pretty much exactly what I want - it masks the bunnies behind the cube but not those closer to the camera than the cube. Thank you so much! One final question - is it possible to change the opacity of the cube through script, for example attaching it to a slider? I tried changing all 4 values in the Color inside the Pass and nothing seemed to happen.