About a week ago, I posted up a progress shot of the shader we’re going to use in the secret areas for Standpoint for Screenshot Saturday. If you didn’t see me post it then, have a look here:
I was asked if I could write up explaining how the shader worked, so that’s what I’ll do, but first, I’ll show you all the finished effect (note: the glow is using HDR of Unity pro).
Ok, so now to the meat of it. How does the shader actually work. I owe a lot of thanks to Simon Wittber for the shader he posted which is what I based my own one on. You can find the full code for my shader at the end of this post, but most of the interesting stuff happens in the surface shader, which I’ve extracted here:
void surf (Input IN, inout SurfaceOutput o) { half2 uv = (IN.uv_MainTex + _Offset) * _Scale ; half r = sqrt (uv.x*uv.x + uv.y*uv.y); half z = sin (r+_Time[1]*_Speed) / (r*10); half4 c = tex2D(_MainTex, IN.uv_MainTex+(z)) * _Color; half al = tex2D(_MainTex, IN.uv_MainTex+(z)).a; float dist = distance(IN.objPos.xyz, _WorldSpaceCameraPos.xyz)/16; o.Albedo = c.rgb ; o.Emission = 1.6* (1-al) / (dist*dist); }
So what’s going on here? My first three lines are almost identical to Simon’s shader apart bar two important changes. Firstly, I made the offset a uniform, so that I could move the centre of the ripples as I saw fit for each object. Secondly, when I calculate z, I effectively divide it by 10, so make the effect more subtle.
Shift the UVs by z, and boom. You have secret shader 1.0. This doesn’t deal well with areas off of the UV islands however, as you can see from the gray areas that would appear in the original. To solve this, I mapped the alpha channel of the texture to the surface’s emission, so the revealed areas would glow, just like the level exits! Originally, this came out looking… well… too shiny, so I also divided the emission by the square of its distance from the camera, so close sections glowed, but far sections didn’t! I’m quite happy with the final effect, and if anyone wants to use it themselves, I’d love to see how it evolves!
- Vethan
Here’s the full shader:
Shader "Toon/Lighted-Ripple" {
Properties {
_Color ("Main Color", Color) = (0.5,0.5,0.5,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_Ramp ("Toon Ramp (RGB)", 2D) = "gray" {}
_Speed ("Speed", Range(2,25.0)) = 1.0
_Scale ("Scale", Range(.5,500.0)) = 1.0
_Offset ("Offset", Range(-1,1.0)) = 0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf ToonRamp vertex:vert
sampler2D _Ramp;
// custom lighting function that uses a texture ramp based
// on angle between light direction and normal
#pragma lighting ToonRamp exclude_path:prepass
inline half4 LightingToonRamp (SurfaceOutput s, half3 lightDir, half atten)
{
#ifndef USING_DIRECTIONAL_LIGHT
lightDir = normalize(lightDir);
#endif
half d = dot (s.Normal, lightDir)*0.5 + 0.5;
half3 ramp = tex2D (_Ramp, float2(d,d)).rgb;
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
c.a = 0;
return c;
}
sampler2D _MainTex;
float4 _Color;
half _Speed;
half _Scale;
half _Offset;
struct Input {
float2 uv_MainTex : TEXCOORD0;
float3 objPos;
};
void vert (inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input,o);
o.objPos = mul (_Object2World, v.vertex);
}
void surf (Input IN, inout SurfaceOutput o) {
half2 uv = (IN.uv_MainTex + _Offset) * _Scale ;
half r = sqrt (uv.x*uv.x + uv.y*uv.y);
half z = sin (r+_Time[1]*_Speed) / (r*10);
half4 c = tex2D(_MainTex, IN.uv_MainTex+(z)) * _Color;
half al = tex2D(_MainTex, IN.uv_MainTex+(z)).a;
float dist = distance(IN.objPos.xyz, _WorldSpaceCameraPos.xyz)/16;
o.Albedo = c.rgb;
o.Emission = 1.6* (1-al) / (dist*dist);
}
ENDCG
}
Fallback "Diffuse"
}