Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Created September 30, 2025 13:06
Show Gist options
  • Save unitycoder/bef1f441e7ce12491e6d87d8ddbe49ce to your computer and use it in GitHub Desktop.
Save unitycoder/bef1f441e7ce12491e6d87d8ddbe49ce to your computer and use it in GitHub Desktop.
Fresnel shaders, HDR, Overlay
Shader "Custom/FresnelOverlay_Add1"
{
Properties
{
[HDR]_RimColor ("Fresnel Color (HDR)", Color) = (3,3,3,1)
_RimPower ("Fresnel Power", Range(0.1, 8)) = 3.0
_RimStrength ("Fresnel Strength", Range(0, 10)) = 1.0
_DoubleSided ("Double-Sided (0=Back,1=Off,2=Front)", Range(0,2)) = 2
_DepthBias ("Depth Bias (to avoid misses)", Float) = 0.0
}
SubShader
{
Tags { "Queue"="Transparent+10" "RenderType"="Transparent" }
// Additive overlay on top of what’s already rendered
Blend One One
ZWrite Off
ZTest Equal // draw only where depth already matches (on the mesh)
Cull Back // default: draw front faces only
ColorMask RGB
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma multi_compile_instancing
#include "UnityCG.cginc"
float4 _RimColor;
float _RimPower;
float _RimStrength;
float _DoubleSided; // 0=Back, 1=Off (both), 2=Front
float _DepthBias;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
float3 worldNrm : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_OUTPUT(v2f, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 worldNrm = UnityObjectToWorldNormal(v.normal);
// Optional tiny depth bias to make ZTest Equal more forgiving
float4 clip = UnityObjectToClipPos(v.vertex);
clip.z += _DepthBias * clip.w;
o.pos = clip;
o.worldPos = worldPos;
o.worldNrm = worldNrm;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// Handle sidedness quickly
float3 N = normalize(i.worldNrm);
float3 V = normalize(_WorldSpaceCameraPos - i.worldPos);
// Which faces to keep:
// Front: dot(N,V) > 0
// Back: dot(N,V) < 0
// Both: no discard
float ndotv = dot(N, V);
if (_DoubleSided < 0.5) // Back faces only
{
if (ndotv > 0) discard;
}
else if (_DoubleSided > 1.5) // Front faces only
{
if (ndotv < 0) discard;
}
// else: both sides
// Fresnel
ndotv = saturate(abs(ndotv)); // abs for double-sided consistency
float rim = pow(1.0 - ndotv, _RimPower);
float3 color = _RimColor.rgb * (rim * _RimStrength);
return float4(color, 1);
}
ENDCG
}
}
Fallback Off
}
Shader "Custom/FresnelOverlay_Add2"
{
Properties
{
[HDR]_RimColor ("Fresnel Color (HDR)", Color) = (3,3,3,1)
_RimPower ("Fresnel Power", Range(0.1, 8)) = 3.0
_RimStrength ("Fresnel Strength", Range(0, 10)) = 1.0
[HDR]_OverlayColor ("Overlay Color (HDR, fill)", Color) = (0,0,0,0)
_DoubleSided ("Double-Sided (0=Back,1=Both,2=Front)", Range(0,2)) = 2
_DepthBias ("Depth Bias (to avoid misses)", Float) = 0.0
}
SubShader
{
Tags { "Queue"="Transparent+10" "RenderType"="Transparent" }
// Additive overlay on top of what's already rendered
Blend One One
ZWrite Off
ZTest Equal // only on the mesh where depth matches
Cull Back
ColorMask RGB
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma multi_compile_instancing
#include "UnityCG.cginc"
float4 _RimColor;
float _RimPower;
float _RimStrength;
float4 _OverlayColor;
float _DoubleSided; // 0=Back, 1=Both, 2=Front
float _DepthBias;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
float3 worldNrm : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_OUTPUT(v2f, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
float4 clip = UnityObjectToClipPos(v.vertex);
clip.z += _DepthBias * clip.w; // tiny bias if needed
o.pos = clip;
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNrm = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 N = normalize(i.worldNrm);
float3 V = normalize(_WorldSpaceCameraPos - i.worldPos);
float ndotv = dot(N, V);
// Sidedness gating
if (_DoubleSided < 0.5) // back faces only
{
if (ndotv > 0) discard;
}
else if (_DoubleSided > 1.5) // front faces only
{
if (ndotv < 0) discard;
}
// Fresnel rim
ndotv = saturate(abs(ndotv)); // abs for double-sided consistency
float rim = pow(1.0 - ndotv, _RimPower);
float3 rimCol = _RimColor.rgb * (rim * _RimStrength);
// Full-surface overlay (set to black to disable)
float3 fillCol = _OverlayColor.rgb;
return float4(rimCol + fillCol, 1);
}
ENDCG
}
}
Fallback Off
}
Shader "Custom/PBR_FresnelRim"
{
Properties
{
_Color ("Base Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB) Alpha (A)", 2D) = "white" {}
_Metallic ("Metallic", Range(0,1)) = 0.0
_Glossiness ("Smoothness", Range(0,1)) = 0.5
[NoScaleOffset]_BumpMap ("Normal Map", 2D) = "bump" {}
_BumpScale ("Normal Scale", Range(0,2)) = 1.0
[HDR]_RimColor ("Fresnel Edge Color (HDR)", Color) = (4,4,4,1)
_RimPower ("Fresnel Power (falloff)", Range(0.1, 8)) = 3.0
_RimStrength ("Fresnel Strength (intensity)", Range(0, 10)) = 1.0
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
LOD 300
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
#pragma multi_compile_instancing
sampler2D _MainTex;
fixed4 _Color;
half _Metallic;
half _Glossiness;
sampler2D _BumpMap;
half _BumpScale;
fixed4 _RimColor;
half _RimPower;
half _RimStrength;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldPos;
float3 viewDir; // Unity provides world-space view direction
};
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_INSTANCING_BUFFER_END(Props)
inline float3 UnpackNormalScaled(sampler2D nmap, float2 uv, float scale)
{
float3 n = UnpackNormal(tex2D(nmap, uv));
n.xy *= scale;
n = normalize(n);
return n;
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = albedo.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = albedo.a;
// Normal mapping (optional)
float3 N = float3(0,0,1);
if (_BumpScale > 0.0001)
N = UnpackNormalScaled(_BumpMap, IN.uv_BumpMap, _BumpScale);
else
N = normalize(o.Normal);
// Fresnel rim: pow(1 - N·V, power)
float3 V = normalize(IN.viewDir);
float ndotv = saturate(dot(N, V));
float rim = pow(1.0 - ndotv, _RimPower);
// Emission drives the glow; HDR color recommended
o.Emission = _RimColor.rgb * rim * _RimStrength;
}
ENDCG
}
FallBack "Diffuse"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment