Search Unity

Global shader to apply transparency on objects depending how far is the camera

Discussion in 'Shaders' started by weyzohorth, Jul 1, 2013.

  1. weyzohorth

    weyzohorth

    Joined:
    Jul 1, 2013
    Posts:
    7
    Hi,

    I'm new to the shader programming, and I have some troubles to do what I need.
    I found several shader on internet, but it seems shaders for materials work differently than global shader.

    So I began to write myself the shader that I want, but it seems to do just nothing (except make the objects with a material using this shader all white (an object without texture)).

    My goal is to use this shader by the camera, not by apply it onto a material manually.

    Otherwise, if you have any ideas to make objects tranparent depending how far is the camera, without modifying their material (and without using Raycasting, because this objects haven't any colliders), I'm pretty interested.
    Also, I would prefer to don't use a script for that either, because I guess it should be pretty expensive in ressources (CPU) which are needed for something else.

    Here, my actual code:
    Code (csharp):
    1.  
    2. Shader "Custom/CamDist" {
    3.     Properties {
    4.         _FadeDistanceMin ("Fade Distance Min", Float) = 10
    5.         _FadeDistanceMax ("Fade Distance Max", Float) = 50
    6.     }
    7.  
    8.     SubShader {
    9.         Tags { "RenderType" = "Transparent" }
    10.         Pass {
    11.             CGPROGRAM
    12.             #pragma vertex vert
    13.             #pragma fragment frag
    14.            
    15.             #include "UnityCG.cginc"
    16.    
    17.             struct v2f {
    18.                 float4  pos : SV_POSITION;
    19.                 fixed4 color : COLOR;
    20.             };
    21.  
    22.             float _FadeDistanceMin;
    23.             float _FadeDistanceMax;
    24.            
    25.             v2f vert (appdata_full v) {
    26.                 v2f o;
    27.                 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    28.                 o.color = v.color;
    29.                 float dist = distance(_WorldSpaceCameraPos, mul(_Object2World, v.vertex));
    30.  
    31.                 if (dist < _FadeDistanceMin)
    32.                     o.color[3] = 0.0f;
    33.                 else if (_FadeDistanceMax < dist)
    34.                     o.color[3] = 1.0f;
    35.                 else
    36.                     o.color[3] = (dist - _FadeDistanceMin) / (_FadeDistanceMax - _FadeDistanceMin);
    37.                 return o;
    38.             }
    39.  
    40.             half4 frag (v2f i) : COLOR {
    41.                 return half4 (i.color);
    42.             }
    43.             ENDCG
    44.         }
    45.     }
    46. }
    47.  
    See you.
     
  2. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,880
    Why dont you use fog to fade your objects away?
     
  3. weyzohorth

    weyzohorth

    Joined:
    Jul 1, 2013
    Posts:
    7
    The fog is to fade objects away (like you said), I want to fade objects near to the camera.
    So, I don't think that could resolve my problem. =/

    Well, I just found a shader which does exactly what I want, except It doesn't work as a global shader (applied by the camera). There is any solution to make it works as global ?

    The shader link
    Code (csharp):
    1.  
    2. Shader "MainMenuHouseShader" {
    3. Properties {
    4.  _Color ("Main Color", Color) = (1,1,1,1)
    5.  _MainTex ("Base (RGB)", 2D) = "white" {}
    6. }
    7. SubShader {
    8.  Cull Off
    9.  //AlphaTest Less 0.6
    10.  Fog { Mode Off }
    11.  Blend SrcAlpha OneMinusSrcAlpha
    12.  LOD 200
    13.  
    14.  CGPROGRAM
    15.  #pragma surface surf Lambert vertex:vert
    16.  
    17.  sampler2D _MainTex;
    18.  fixed4 _Color;
    19.  
    20.  struct Input {
    21.  float2 uv_MainTex;
    22.  float3 fooAlpha;
    23.  };
    24.  
    25.  void vert (inout appdata_full v, out Input o) {
    26.    // Transform to camera space
    27.    float3 foo = mul(UNITY_MATRIX_MVP, v.vertex);
    28.    o.fooAlpha = foo.z * 0.15;
    29.  }
    30.  
    31.  void surf (Input IN, inout SurfaceOutput o) {
    32.    fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    33.    o.Albedo = c.rgba;
    34.    o.Alpha = IN.fooAlpha;
    35.  }
    36.  ENDCG
    37. }
    38.  
    39. Fallback "VertexLit"
    40. }
    41.  
     
    Last edited: Jul 2, 2013
  4. XGundam05

    XGundam05

    Joined:
    Mar 29, 2012
    Posts:
    473
    So, are you looking for something that makes an object transparent when it would be blocking the majority of the camera's view? For example, make a person transparent if they're so close to the camera and between it and the camera's target (hence they would block a majority of the view). I don't have a solution, I just thought I might try to help clarify.
     
  5. weyzohorth

    weyzohorth

    Joined:
    Jul 1, 2013
    Posts:
    7
    Yup, it's that I want.
    (A good part is avoid by raycasting and repositioning the camera, but there are many objects without collider which are still blocking the view)
     
  6. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,880
    Oh, so you want to fade objects when they are close to camera.
    Easy enough, you prepare your shaders with a float transparency property(0-1 range) instead of builtin textures alpha channel and then you control this value globally through scripting(you decrease the transparency when the object is close to camera or whatever you need)

    Or, you can avoid the scripting part totally and calculate the whole thing in your shaders as well.

    EDIT: I think you can even do this through image effects, i should try this.
     
  7. weyzohorth

    weyzohorth

    Joined:
    Jul 1, 2013
    Posts:
    7
    How I should apply the float transparency if is not on the texture alpha channel itself ?
    I don't get it.

    I will take a look on this image effects. It seems, it should be something to do with this.

    Thanks.
     
  8. Lulucifer

    Lulucifer

    Joined:
    Jul 8, 2012
    Posts:
    358
    Depth is a choice,you can render two frame ,one with your transparent,then blend they depends on Z Depth
     
  9. Annihlator

    Annihlator

    Joined:
    Oct 15, 2012
    Posts:
    378
    An example i thought up consists like this;
    one script attached to the player which will check for all fadeable objects at start, by checking if they have a tag "Fadeable"
    on each update for all objects within range it will create a float based on minimum and maximum clip distances
    this float will be assigned to all detected objects using renderer.material.SetFloat, to the shader's _Fader value I added.

    It's probably not the cleanest example, but it seems quite lightweight and can be added to most shaders without a lot of hassle.

    Shader Code:
    ANY shader that uses a _Fader value can be used.
    Code (csharp):
    1.  
    2. Shader "Custom/ProximityFade" {
    3.     Properties {
    4.         _MainTex ("Base (RGB)", 2D) = "white" {}
    5.         _Fader ("Fader Slider", Float) = 0
    6.         _MinOpacity ("Minimum Opacity", Range(0,1)) = 0.1
    7.     }
    8.     SubShader {
    9.         Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    10.         LOD 200
    11.         ZWrite Off
    12.         ZTest Less
    13.         Cull Back
    14.        
    15.         CGPROGRAM
    16.         #pragma surface surf Lambert alpha
    17.        
    18.  
    19.         sampler2D _MainTex;
    20.         float _Fader;
    21.         float _MinOpacity;
    22.  
    23.         struct Input {
    24.             float2 uv_MainTex;
    25.         };
    26.  
    27.         void surf (Input IN, inout SurfaceOutput o) {
    28.             half4 c = tex2D (_MainTex, IN.uv_MainTex);
    29.             o.Albedo = c.rgb;
    30.             _Fader = max(_MinOpacity,_Fader);
    31.             o.Alpha = c.a*_Fader;
    32.         }
    33.         ENDCG
    34.     }
    35.     FallBack "Diffuse"
    36. }
    37.  
    Script Code: [Scriptname: Fader.cs]
    This is best attached to the camera or a scene manager of some sort.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class Fader : MonoBehaviour {
    7.     public bool UseFadeableTags;
    8.     public Transform playerobject;
    9.     public Shader fadeshader;
    10.     public float MinClip = 0.5f;
    11.     public float MaxClip = 2f;
    12.     private List<Transform> FadeableObjects;
    13.     private Vector2 sqrdist;
    14.    
    15.     bool inRange(Transform firstTransform, Transform otherTransform, float distance)
    16.     {
    17.         return (firstTransform.position - otherTransform.position).sqrMagnitude < distance * distance;
    18.     }
    19.    
    20.     bool inPlayerRange(Transform target, float distance)
    21.     {
    22.         return (playerobject.position - target.position).sqrMagnitude < distance*distance;
    23.     }
    24.  
    25.     // Use this for initialization
    26.     void Start () {
    27.    
    28.     if (GameObject.Find("Player"))
    29.             playerobject = GameObject.Find("Player").transform;
    30.     FadeableObjects = new List<Transform>();
    31.         GameObject[]go;
    32.         if (UseFadeableTags == true)
    33.             {
    34.             go = GameObject.FindGameObjectsWithTag("Fadeable"); //looked up by tag
    35.             }
    36.         else
    37.             {
    38.             go = UnityEngine.GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[]; // iterating through ALL objects
    39.             }
    40.         if (fadeshader != null) // if fadeshader is selected
    41.         {
    42.         foreach(GameObject FadeableObject in go)
    43.         {
    44.             if (FadeableObject.renderer){
    45.             if (FadeableObject.renderer.material.shader == fadeshader)
    46.             FadeableObjects.Add(FadeableObject.transform);
    47.             }
    48.         }}
    49.             else // if no fadeshader is selected:
    50.         {
    51.             foreach(GameObject FadeableObject in go)
    52.             {
    53.             FadeableObjects.Add(FadeableObject.transform);
    54.             Debug.Log("Added "+FadeableObject.name);
    55.             }
    56.         }
    57.     sqrdist = new Vector2(MinClip*MinClip, MaxClip*MaxClip);
    58.     }
    59.    
    60.    
    61.     // Update is called once per frame
    62.     void Update () {
    63.     foreach(Transform FadeableObject in FadeableObjects)
    64.         {
    65.             if (inPlayerRange(FadeableObject,sqrdist.y))
    66.             {
    67.             float FadeValue = Mathf.InverseLerp(sqrdist.x,sqrdist.y,(FadeableObject.position - playerobject.position).sqrMagnitude);
    68.             FadeableObject.renderer.material.SetFloat("_Fader", FadeValue);
    69.             }
    70.         }
    71.     }
    72. }
    73.  
    Hope it'll help you out a bit :)
    This method is mostly based on an object's transform position, but i'd guess it's a quite effective method for objects which aren't too big of a size.The script could always be modified further for additional checks

    I also put a webdemo online;
    www.annihlator.nl/Unity/Shared/Faderweb/FaderWeb.html

    EDIT:
    I know you preferred a way without scripts, but I believe this is a lightweight method because;
    - When you use Tags, it will only make an array of the objects which can be faded at all.
    - It only uses a (relatively) cheap comparison of positions between the objects which could possibly be faded (so no distances calculated of other surfaces, also due to the shader selector)
    - it does not rely on any triggers or colliders

    possible downside as i said above can be accuracy.
     
    Last edited: Jul 3, 2013
  10. weyzohorth

    weyzohorth

    Joined:
    Jul 1, 2013
    Posts:
    7
    Well, i guess this method can be relatively efficient.
    Even if I guess, it can't works properly for the grass or others kinds of vegetation (big array).
    But that should does the work for tree and others massive objects.

    But still better than the solution I was thinking.

    Thank you, it will be useful :)

    Edit @Lulucifer: Interesting idea, even if it seems to require a knowledge that I don't have actually ^^
     
    Last edited: Jul 3, 2013
  11. Annihlator

    Annihlator

    Joined:
    Oct 15, 2012
    Posts:
    378
    Thanks weyzohorth, glad I could offer some insight :)

    I love to think along so i hope you don't mind me being so blunt to try and thing along about the vegetation.
    Though i'm affraid that you probably don't have Unity Pro, else i would've suggested to use depth textures for sure.

    Perhaps that for the vegetation you could employ triggers, then you could use onTriggerEnter events to add the vegetation which is close enough to the character to a list, and remove it again when it's out of range. only having to calculate the distance for the parts that have been added to the list. And i believe onTriggerEnter events are not very expensive though sadly you'd have to start using colliders.

    I decided to try and optimize the script i had already posted, there's just a few minor adjustments.
    I added a seperate lookup for the vegetation but also added an updateinterval function.
    Using modulo (%) over Time.frameCount allows us to only process the calculations every so-many frames.
    To employ this change all you'd have to do is update the Fader.cs with the code below and adjust your vegetation's shaders to also use the _Fade variable.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class Fader : MonoBehaviour {
    7.     public bool IntervalUpdate = true;
    8.     public float UpdateInterval = 4;
    9.     public bool UseFadeableTags;
    10.     public Transform playerobject;
    11.     public Shader fadeshader;
    12.     public Shader vegetationfadeshader;
    13.     public float MinClip = 0.5f;
    14.     public float MaxClip = 2f;
    15.     private List<Transform> FadeableObjects;
    16.     private List<Transform> VegetationObjects;
    17.     private Vector2 sqrdist;
    18.     public bool ScanVegetationTags;
    19.    
    20.     bool inRange(Transform firstTransform, Transform otherTransform, float distance)
    21.     {
    22.         return (firstTransform.position - otherTransform.position).sqrMagnitude < distance * distance;
    23.     }
    24.    
    25.     bool inPlayerRange(Transform target, float distance)
    26.     {
    27.         return (playerobject.position - target.position).sqrMagnitude < distance*distance;
    28.     }
    29.  
    30.     // Use this for initialization
    31.     void Start () {
    32.    
    33.     if (GameObject.Find("Player"))
    34.             playerobject = GameObject.Find("Player").transform;
    35.     FadeableObjects = new List<Transform>();
    36.         GameObject[]go;
    37.         if (UseFadeableTags == true)
    38.             {
    39.             go = GameObject.FindGameObjectsWithTag("Fadeable"); //looked up by tag
    40.             }
    41.         else
    42.             {
    43.             go = UnityEngine.GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[]; // iterating through ALL objects
    44.             }
    45.         if (fadeshader != null) // if fadeshader is selected
    46.         {
    47.         foreach(GameObject FadeableObject in go)
    48.         {
    49.             if (FadeableObject.renderer){
    50.             if (FadeableObject.renderer.material.shader == fadeshader)
    51.             FadeableObjects.Add(FadeableObject.transform);
    52.             }
    53.         }}
    54.             else // if no fadeshader is selected:
    55.         {
    56.             foreach(GameObject FadeableObject in go)
    57.             {
    58.             FadeableObjects.Add(FadeableObject.transform);
    59.             }
    60.         }
    61.        
    62.         if (ScanVegetationTags == true)
    63.         {
    64.             VegetationObjects = new List<Transform>();
    65.             GameObject[]go2;
    66.             go = GameObject.FindGameObjectsWithTag("Vegetation"); // looked up by Vegetation tag
    67.             if (vegetationfadeshader != null)
    68.             {
    69.                 foreach(GameObject FadeableVegetation in go)
    70.                 {
    71.                     if (FadeableVegetation.renderer)
    72.                     if (FadeableVegetation.renderer.material.shader == vegetationfadeshader)
    73.                     VegetationObjects.Add (FadeableVegetation.transform);
    74.                 }
    75.             }
    76.             else
    77.             {
    78.                 foreach(GameObject FadeableVegetation in go)
    79.                 {
    80.                     if (FadeableVegetation.renderer)
    81.                     VegetationObjects.Add (FadeableVegetation.transform);
    82.                 }
    83.             }
    84.            
    85.         }
    86.        
    87.     sqrdist = new Vector2(MinClip*MinClip, MaxClip*MaxClip);
    88.     }
    89.    
    90.    
    91.     // Update is called once per frame
    92.     void Update () {
    93.     if ((Time.frameCount%UpdateInterval == 0  IntervalUpdate) || !IntervalUpdate) // If timerinterval activated AND interval reacher, OR no Interval set
    94.         {
    95.     foreach(Transform FadeableObject in FadeableObjects)
    96.         {
    97.             if (inPlayerRange(FadeableObject,sqrdist.y))
    98.             {
    99.             float FadeValue = Mathf.InverseLerp(sqrdist.x,sqrdist.y,(FadeableObject.position - playerobject.position).sqrMagnitude);
    100.             FadeableObject.renderer.material.SetFloat("_Fader", FadeValue);
    101.             }
    102.         }
    103.     foreach(Transform FadeableVegetation in VegetationObjects)
    104.             {
    105.                 if (inPlayerRange(FadeableVegetation,sqrdist.y))
    106.                 {
    107.                 float FadeValue = Mathf.InverseLerp(sqrdist.x,sqrdist.y,(FadeableVegetation.position - playerobject.position).sqrMagnitude);
    108.                 FadeableVegetation.renderer.material.SetFloat("_Fader", FadeValue);
    109.                 }
    110.             }
    111.         }
    112.     }
    113. }
    114.  
    I'm trying to find a function which only returns all gameobjects within a certain range, but am still unable to thus far without using colliders or triggers.
    Nevertheless, doing the calculations every 4th frame should still give smooth transitions as long as the framerate is decent and already saves 3/4rd of the script's processing time. So that's always neat!

    EDIT: I've uploaded a few more webplayers again so people can test the difference with different intervals and ranges.
    www.annihlator.nl/Unity/Shared/FaderTests
    open whichever html you favor, naming is as follows:
    frameInt is interval, min is minimum fade distance, Max is maximum fade distance.
    EDIT2: tests on a gt520 and atom d525 with these scenes show 20-24fps without interval and 28-30 with 2, 34-38 with 8 (however you can more clearly see the interval). the scene has about 100 objects with fadeable tag.
     
    Last edited: Jul 3, 2013
  12. weyzohorth

    weyzohorth

    Joined:
    Jul 1, 2013
    Posts:
    7
    Wow =O
    Really nice !
    I was asking to myself, if this shouldn't be to heavy to use on each frame. Well, you answered to that too.
    I'm always hesitating about use that with the vegetation, but I'll try.

    Anyway, that's great !
    Thanks you a lot.

    Edit: I was already thinking about the triggers for the vegetation, but I guess, we will do nothing about that.
    Or maybe, only for the PC version, and nothing for the Mobile one, as you noticed the low framerate.
    Thanks for the fps tests :)
     
    Last edited: Jul 3, 2013
  13. Annihlator

    Annihlator

    Joined:
    Oct 15, 2012
    Posts:
    378
    i think using triggers would benefit performance,
    as the list wont be cluttered by objects out of range. :)