Unity Shader: Spherical Mask
I'm a big fan of Beyond Eyes game and the way the world takes form while the little girl walks around.
Last time we looked at the dissolve effect this time we take a look at Spherical Mask effect.
The two effects combined together will let us create a custom shader that emulates the Beyond Eyes effect.
Let's begin!
The image above is pretty self explanatory, we have a player object that has a radius of action, every pixels inside its radius of action will be colored while the others stay in grayscale.
Here's the final code:
Line 19 get a reference to our shader from the material of our object and pass the player position (player.transform.position).
Last time we looked at the dissolve effect this time we take a look at Spherical Mask effect.
The two effects combined together will let us create a custom shader that emulates the Beyond Eyes effect.
Let's begin!
Spherical Mask
The image above is pretty self explanatory, we have a player object that has a radius of action, every pixels inside its radius of action will be colored while the others stay in grayscale.
Let's begin with adding two input propeties to our shader:
1 2 3 4 5 6 7 8 9 | Properties { //...other shader properties //Spherical Mask properties _PlayerPosition("Player Position", Vector) = (0, 0, 0, 0) _Radius("Mask Radius", Range(0, 100)) = 0 _Softness("Mask Softness", Range(0, 100)) = 0 } |
- _PlayerPosition passes the player postion to the shader
- _Radius is the player radius of action, anything inside of it will be colored
- _Softness controls how soft is the transition between grayscaled pixels and the colored ones
Now in the surf function we'll do the following things:
- Sample the color of the texture before making it grayscaled
- Compute the color of the texture in grayscale so that by default the texture will appear in grayscale
- For any vertex of the plane, compute its distance from the player
- Now that we have the distance, check if the vertex is inside the player radius of action
- Apply the softness
- For every pixel, lerp between the grayscale texture to the colored one by how near is the vertex to the player
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | void surf (Input IN, inout SurfaceOutputStandard o) { //Get the color of the texture fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; //Compute the color of the texture in grayscale float grayscale = (c.r + c.g + c.b) / 3; fixed3 c_gray = fixed3(grayscale, grayscale, grayscale); //For any vertex of the plane, compute its distance from the player half d = distance(_PlayerPosition, IN.worldPos); //Check if the vertex is inside the player radius of action half near_value = d - _Radius; //Apply the softness near_value = near_value / -_Softness; //Clamp the near_value_soft to a range (0, 1) near_value = saturate(near_value); //Lerp from grayscale to the colored texture by the amount of near_value fixed4 lerpColor = lerp(fixed4(c_gray, 1), c, near_value); //Apply the lerpColor o.Albedo = lerpColor.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; // Metallic and smoothness come from slider variables o.Albedo = lerpColor.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } |
Here's the final code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | Shader "Mistwork/Spherical Mask" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 //Spherical Mask properties _PlayerPosition("Player Position", Vector) = (0, 0, 0, 0) _Radius("Mask Radius", Range(0, 100)) = 0 _Softness("Mask Softness", Range(0, 100)) = 0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; //The object world-space position to which this shader is applied float3 worldPos; }; half _Glossiness; half _Metallic; fixed4 _Color; float4 _PlayerPosition; half _Radius; half _Softness; void surf (Input IN, inout SurfaceOutputStandard o) { //Get the color of the texture fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; //Compute the color of the texture in grayscale float grayscale = (c.r + c.g + c.b) / 3; fixed3 c_gray = fixed3(grayscale, grayscale, grayscale); //For any vertex of the plane, compute its distance from the player half d = distance(_PlayerPosition, IN.worldPos); //Check if the vertex is inside the player radius of action half near_value = d - _Radius; //Apply the softness near_value = near_value / -_Softness; //Clamp the near_value_soft to a range (0, 1) near_value = saturate(near_value); //Lerp from grayscale to the colored texture by the amount of near_value fixed4 lerpColor = lerp(fixed4(c_gray, 1), c, near_value); //Apply the lerpColor o.Albedo = lerpColor.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; // Metallic and smoothness come from slider variables o.Albedo = lerpColor.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } |
How to pass the Player position to the shader
First, attach our Spherical Mask shader to the object we want to get colored as the player moves.
To pass the player position to the shader we use a pretty simple C# code that we have to attach to the same object.
Here's the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class SphericalMask : MonoBehaviour { [SerializeField] private GameObject player; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { GetComponent<Renderer>().material.SetVector("_PlayerPosition", player.transform.position); } } |
Line 19 get a reference to our shader from the material of our object and pass the player position (player.transform.position).
Comments
Post a Comment