Before me trying to explain you how i could achieve this, just have a look at this video.
Beautiful isn't it?
Lets jump straight into the concept.
If you see in that video, all the objects have their own textures, and as the time passes due to the show effect, a white texture is put on them. Now the question is how?
Let me divide this concept into 3 parts,
1. Take Normals of each vertex(of trees) in vertex input, and convert into world space in your vertex shader program.
struct vertexInput
{
float4 vertex: POSITION;
float4 texcoorda: TEXCOORD0;
float4 normal: NORMAL; //Taking Normal
};
vertexOutput vert(vertexInput v)
{
vertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
o.texcoorda.xy = v.texcoorda + _Texture_ST.wz;
o.normalWorld = normalize(mul(v.normal, unity_ObjectToWorld)); //Converting normals to world space
return o;
}
2. Declare a global variable which tell you the direction in which the snow is falling. this direction is in the world space(so this is the reason we are converting our normals into world space in the first step).
Properties
{
.
.
.
.
_snowDirection("Snow Direction", Vector) = (0,1,0)
.
.
}
3. The dot product of the normals and snow fall direction is >0 for the parts of the mesh exposed to the snow, and the dot product of the normals and snow fall direction vector is < 0 for the parts which are not exposed to the snow. This is the core logic of this code.
So here i am clamping the dot product value, from 0 to 1 and multiplying with the snow texture which i have already created in global variable(_NTexture("Basic Texture", 2D) = "white" {} ). And this multiplied value, i am adding to the original mesh texture.
half4 frag(vertexOutput i) : COLOR
{
_snowDirection.y = _effectValue;
float dotValue = clamp(dot(i.normalWorld.xyz,_snowDirection.xyz), 0, 1);
return (dotValue * tex2D(_NTexture, i.texcoorda)) + tex2D(_Texture, i.texcoorda);
}
So lets consider the parts which are not exposed to the snow. Dot product of this normal vector and snow is < 0. So when we multiply this with snow Texture value(_NTexture("Basic Texture", 2D) = "white" {}), it is 0 and by adding this 0 to the original mesh texture, there is no change to the texture. So unexposed parts of the mesh don't get the snow texture.
Now lets consider the exposed part whose dot product is > 0. This get some white color giving you that snow effect which you are looking for.
4. Then how did i achieve that gradual increase in snow effect? It is rather simple.
I have already defined the snow fall direction value as 0,1,0,0. By increasing the y value of this world snow fall direction value using C#, I can achieve that effect.
using System.Collections.Generic;
using UnityEngine;
public class SnowFall : MonoBehaviour
{
float effectValueIncrement = 0f;
// Update is called once per frame
void Update()
{
if (effectValueIncrement < 10.0f)
{
effectValueIncrement += Time.deltaTime / 10.0f;
GetComponent<MeshRenderer>().material.SetFloat("_effectValue", effectValueIncrement);
}
}
}
So here is the entire shader code. For snow texture i did't drop any texture on to the inspector rather used the "white" default texture for now(NTexture).
Shader "ShaderLearning/snowEffect"
{
Properties
{
_Color("Main Color", Color) = (1,1,1,1)
_Texture("Basic Texture", 2D) = "white" {}
_NTexture("Basic Texture", 2D) = "white" {} //Plane white texture which depicts snow- Snow Texture
_snowDirection("Snow Direction", Vector) = (0,1,0)
_effectValue("EffectValue",float) = 0
}
Subshader
{
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform half4 _Color;
uniform sampler2D _Texture;
uniform sampler2D _NTexture;
uniform float4 _Texture_ST;
uniform float4 _snowDirection;
uniform float _effectValue;
struct vertexInput
{
float4 vertex: POSITION;
float4 texcoorda: TEXCOORD0;
float4 normal: NORMAL; //Getting normal
};
struct vertexOutput
{
float4 pos : SV_POSITION;
float4 texcoorda: TEXCOORD0;
float4 normalWorld : TEXCOORD1;
};
vertexOutput vert(vertexInput v)
{
vertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
o.texcoorda.xy = v.texcoorda + _Texture_ST.wz;
o.normalWorld = normalize(mul(v.normal, unity_ObjectToWorld)); //Converting normal to world space
return o;
}
half4 frag(vertexOutput i) : COLOR
{
_snowDirection.y = _effectValue;
float dotValue = clamp(dot(i.normalWorld.xyz,_snowDirection.xyz), 0, 1);
return (dotValue * tex2D(_NTexture, i.texcoorda)) + tex2D(_Texture, i.texcoorda);
}
ENDCG
}
}
}
And here is the C# code to create that gradual effect.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SnowFall : MonoBehaviour
{
float effectValueIncrement = 0f;
// Update is called once per frame
void Update()
{
if (effectValueIncrement < 10.0f)
{
effectValueIncrement += Time.deltaTime / 10.0f;
GetComponent<MeshRenderer>().material.SetFloat("_effectValue", effectValueIncrement);
}
}
}
Thanks and Happy coding!!! :)
Comentarios