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

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:

Shader "Custom/CamDist" {
	Properties {
		_FadeDistanceMin ("Fade Distance Min", Float) = 10
		_FadeDistanceMax ("Fade Distance Max", Float) = 50
	}

	SubShader {
		Tags { "RenderType" = "Transparent" }
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
   
			struct v2f {
				float4  pos : SV_POSITION;
		    	fixed4 color : COLOR;
		    };

			float _FadeDistanceMin;
			float _FadeDistanceMax;
			
			v2f vert (appdata_full v) {
	        	v2f o;
	        	o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
	        	o.color = v.color;
				float dist = distance(_WorldSpaceCameraPos, mul(_Object2World, v.vertex));

				if (dist < _FadeDistanceMin)
					o.color[3] = 0.0f;
				else if (_FadeDistanceMax < dist)
					o.color[3] = 1.0f;
				else
					o.color[3] = (dist - _FadeDistanceMin) / (_FadeDistanceMax - _FadeDistanceMin);
				return o;
			}

			half4 frag (v2f i) : COLOR {
			    return half4 (i.color);
			}
			ENDCG
		}
	}
}

See you.

Why dont you use fog to fade your objects away?

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

Shader "MainMenuHouseShader" {
Properties {
 _Color ("Main Color", Color) = (1,1,1,1)
 _MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
 Cull Off
 //AlphaTest Less 0.6
 Fog { Mode Off }
 Blend SrcAlpha OneMinusSrcAlpha
 LOD 200

 CGPROGRAM
 #pragma surface surf Lambert vertex:vert

 sampler2D _MainTex;
 fixed4 _Color;

 struct Input {
 float2 uv_MainTex;
 float3 fooAlpha;
 };

 void vert (inout appdata_full v, out Input o) {
   // Transform to camera space
   float3 foo = mul(UNITY_MATRIX_MVP, v.vertex);
   o.fooAlpha = foo.z * 0.15;
 }

 void surf (Input IN, inout SurfaceOutput o) {
   fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
   o.Albedo = c.rgba;
   o.Alpha = IN.fooAlpha;
 }
 ENDCG
}

Fallback "VertexLit"
}

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.

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)

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.

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.

Depth is a choice,you can render two frame ,one with your transparent,then blend they depends on Z Depth

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.

Shader "Custom/ProximityFade" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_Fader ("Fader Slider", Float) = 0
		_MinOpacity ("Minimum Opacity", Range(0,1)) = 0.1
	}
	SubShader {
		Tags { "Queue"="Transparent" "RenderType"="Transparent" }
		LOD 200
		ZWrite Off
		ZTest Less
		Cull Back
		
		CGPROGRAM
		#pragma surface surf Lambert alpha
		

		sampler2D _MainTex;
		float _Fader;
		float _MinOpacity;

		struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			o.Albedo = c.rgb;
			_Fader = max(_MinOpacity,_Fader);
			o.Alpha = c.a*_Fader;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

Script Code: [Scriptname: Fader.cs]
This is best attached to the camera or a scene manager of some sort.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Fader : MonoBehaviour {
	public bool UseFadeableTags;
	public Transform playerobject;
	public Shader fadeshader;
	public float MinClip = 0.5f;
	public float MaxClip = 2f;
	private List<Transform> FadeableObjects;
	private Vector2 sqrdist;
	
	bool inRange(Transform firstTransform, Transform otherTransform, float distance) 
	{
		return (firstTransform.position - otherTransform.position).sqrMagnitude < distance * distance;
	}
	
	bool inPlayerRange(Transform target, float distance)
	{
		return (playerobject.position - target.position).sqrMagnitude < distance*distance;
	}

	// Use this for initialization
	void Start () {
	
	if (GameObject.Find("Player"))
			playerobject = GameObject.Find("Player").transform;
	FadeableObjects = new List<Transform>();
		GameObject[]go;
		if (UseFadeableTags == true)
			{
			go = GameObject.FindGameObjectsWithTag("Fadeable"); //looked up by tag
			}
		else
			{
			go = UnityEngine.GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[]; // iterating through ALL objects
			}
		if (fadeshader != null) // if fadeshader is selected
		{
		foreach(GameObject FadeableObject in go)
		{
			if (FadeableObject.renderer){
		    if (FadeableObject.renderer.material.shader == fadeshader)
		    FadeableObjects.Add(FadeableObject.transform);
			}
		}}
			else // if no fadeshader is selected:
		{
			foreach(GameObject FadeableObject in go)
			{
		    FadeableObjects.Add(FadeableObject.transform);
			Debug.Log("Added "+FadeableObject.name);
			}
		}
	sqrdist = new Vector2(MinClip*MinClip, MaxClip*MaxClip);
	}
	
	
	// Update is called once per frame
	void Update () {
	foreach(Transform FadeableObject in FadeableObjects)
		{
			if (inPlayerRange(FadeableObject,sqrdist.y))
			{
			float FadeValue = Mathf.InverseLerp(sqrdist.x,sqrdist.y,(FadeableObject.position - playerobject.position).sqrMagnitude);
			FadeableObject.renderer.material.SetFloat("_Fader", FadeValue);
			}
		}
	}
}

Hope it’ll help you out a bit :slight_smile:
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.

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 :slight_smile:

Edit @Lulucifer: Interesting idea, even if it seems to require a knowledge that I don’t have actually ^^

Thanks weyzohorth, glad I could offer some insight :slight_smile:

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.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public class Fader : MonoBehaviour {
	public bool IntervalUpdate = true;
	public float UpdateInterval = 4;
    public bool UseFadeableTags;
    public Transform playerobject;
    public Shader fadeshader;
	public Shader vegetationfadeshader;
    public float MinClip = 0.5f;
    public float MaxClip = 2f;
    private List<Transform> FadeableObjects;
	private List<Transform> VegetationObjects;
    private Vector2 sqrdist;
	public bool ScanVegetationTags;
    
    bool inRange(Transform firstTransform, Transform otherTransform, float distance) 
    {
        return (firstTransform.position - otherTransform.position).sqrMagnitude < distance * distance;
    }
    
    bool inPlayerRange(Transform target, float distance)
    {
        return (playerobject.position - target.position).sqrMagnitude < distance*distance;
    }
 
    // Use this for initialization
    void Start () {
    
    if (GameObject.Find("Player"))
            playerobject = GameObject.Find("Player").transform;
    FadeableObjects = new List<Transform>();
        GameObject[]go;
        if (UseFadeableTags == true)
            {
            go = GameObject.FindGameObjectsWithTag("Fadeable"); //looked up by tag
            }
        else
            {
            go = UnityEngine.GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[]; // iterating through ALL objects
            }
        if (fadeshader != null) // if fadeshader is selected
        {
        foreach(GameObject FadeableObject in go)
        {
            if (FadeableObject.renderer){
            if (FadeableObject.renderer.material.shader == fadeshader)
            FadeableObjects.Add(FadeableObject.transform);
            }
        }}
            else // if no fadeshader is selected:
        {
            foreach(GameObject FadeableObject in go)
            {
            FadeableObjects.Add(FadeableObject.transform);
            }
        }
		
		if (ScanVegetationTags == true)
		{
			VegetationObjects = new List<Transform>();
			GameObject[]go2;
			go = GameObject.FindGameObjectsWithTag("Vegetation"); // looked up by Vegetation tag
			if (vegetationfadeshader != null)
			{
				foreach(GameObject FadeableVegetation in go)
				{
					if (FadeableVegetation.renderer)
					if (FadeableVegetation.renderer.material.shader == vegetationfadeshader)
					VegetationObjects.Add (FadeableVegetation.transform);
				}
			}
			else
			{
				foreach(GameObject FadeableVegetation in go)
				{
					if (FadeableVegetation.renderer)
					VegetationObjects.Add (FadeableVegetation.transform);
				}
			}
			
		}
		
    sqrdist = new Vector2(MinClip*MinClip, MaxClip*MaxClip);
    }
    
    
    // Update is called once per frame
    void Update () {
	if ((Time.frameCount%UpdateInterval == 0  IntervalUpdate) || !IntervalUpdate) // If timerinterval activated AND interval reacher, OR no Interval set
		{
    foreach(Transform FadeableObject in FadeableObjects)
        {
            if (inPlayerRange(FadeableObject,sqrdist.y))
            {
            float FadeValue = Mathf.InverseLerp(sqrdist.x,sqrdist.y,(FadeableObject.position - playerobject.position).sqrMagnitude);
            FadeableObject.renderer.material.SetFloat("_Fader", FadeValue);
            }
        }
	foreach(Transform FadeableVegetation in VegetationObjects)
			{
				if (inPlayerRange(FadeableVegetation,sqrdist.y))
				{
				float FadeValue = Mathf.InverseLerp(sqrdist.x,sqrdist.y,(FadeableVegetation.position - playerobject.position).sqrMagnitude);
				FadeableVegetation.renderer.material.SetFloat("_Fader", FadeValue);
				}
			}
		}
    }
}

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.

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 :slight_smile:

i think using triggers would benefit performance,
as the list wont be cluttered by objects out of range. :slight_smile: