Skip to content

Instantly share code, notes, and snippets.

@nevarman
Created May 9, 2017 11:39
Show Gist options
  • Save nevarman/62d26fba6f529ec687eb9ad8cd3c7eba to your computer and use it in GitHub Desktop.
Save nevarman/62d26fba6f529ec687eb9ad8cd3c7eba to your computer and use it in GitHub Desktop.
//Source: https://farfarer.com/blog/2013/02/11/pre-integrated-skin-shader-unity-3d/
Shader "Custom/Skin Shader" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Diffuse (RGB)", 2D) = "white" {}
_SpecularTex ("Specular (R) Gloss (G) SSS Mask (B)", 2D) = "yellow" {}
_BumpMap ("Normal (Normal)", 2D) = "bump" {}
// BRDF Lookup texture, light direction on x and curvature on y.
_BRDFTex ("BRDF Lookup (RGB)", 2D) = "gray" {}
// Curvature scale. Multiplier for the curvature - best to keep this very low - between 0.02 and 0.002.
_CurvatureScale ("Curvature Scale", Float) = 0.005
// Controller for fresnel specular mask. For skin, 0.028 if in linear mode, 0.2 for gamma mode.
_Fresnel ("Fresnel Value", Float) = 0.2
// Which mip-map to use when calculating curvature. Best to keep this between 1 and 2.
_BumpBias ("Normal Map Blur Bias", Float) = 1.5
}
SubShader{
Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf SkinShader fullforwardshadows
#pragma target 3.0
// Bit complex for non-desktop.
#pragma only_renderers d3d9 d3d11 opengl
// Required for tex2Dlod function.
#pragma glsl
struct SurfaceOutputSkinShader {
fixed3 Albedo;
fixed3 Normal;
fixed3 NormalBlur;
fixed3 Emission;
fixed3 Specular;
fixed Alpha;
float Curvature;
};
struct Input
{
float2 uv_MainTex;
float3 worldPos;
float3 worldNormal;
INTERNAL_DATA
};
sampler2D _MainTex, _SpecularTex, _BumpMap, _BRDFTex;
float _BumpBias, _CurvatureScale, _Fresnel;
void surf (Input IN, inout SurfaceOutputSkinShader o)
{
float4 albedo = tex2D ( _MainTex, IN.uv_MainTex );
o.Albedo = albedo.rgb;
o.Normal = UnpackNormal ( tex2D ( _BumpMap, IN.uv_MainTex ) );
o.Specular = tex2D ( _SpecularTex, IN.uv_MainTex ).rgb;
// Calculate the curvature of the model dynamically.
// Get a mip of the normal map to ignore any small details for regular shading.
o.NormalBlur = UnpackNormal( tex2Dlod ( _BumpMap, float4 ( IN.uv_MainTex, 0.0, _BumpBias ) ) );
// Transform it back into a world normal so we can get good derivatives from it.
float3 worldNormal = WorldNormalVector( IN, o.NormalBlur );
// Get the scale of the derivatives of the blurred world normal and the world position.
// From these it's possible to work out the rate of change of the surface normal; or it's curvature.
#if SHADER_API_D3D11
// In DX11, ddx_fine should give nicer results.
float deltaWorldNormal = length( abs(ddx_fine(worldNormal)) + abs(ddy_fine(worldNormal)) );
float deltaWorldPosition = length( abs(ddx_fine(IN.worldPos)) + abs(ddy_fine(IN.worldPos)) );
#else
// Otherwise stick with ddx or dFdx, which can be replaced with fwidth.
float deltaWorldNormal = length( fwidth( worldNormal ) );
float deltaWorldPosition = length( fwidth ( IN.worldPos ) );
#endif
o.Curvature = ( deltaWorldNormal / deltaWorldPosition ) * _CurvatureScale;
}
inline fixed4 LightingSkinShader( SurfaceOutputSkinShader s, fixed3 lightDir, fixed3 viewDir, fixed atten )
{
viewDir = normalize( viewDir );
lightDir = normalize( lightDir );
s.Normal = normalize( s.Normal );
s.NormalBlur = normalize( s.NormalBlur );
float NdotL = dot( s.Normal, lightDir );
float3 h = normalize( lightDir + viewDir );
float specBase = saturate( dot( s.Normal, h ) );
float fresnel = pow( 1.0 - dot( viewDir, h ), 5.0 );
fresnel += _Fresnel * ( 1.0 - fresnel );
float spec = pow( specBase, s.Specular.g * 128 ) * s.Specular.r * fresnel;
float2 brdfUV;
float NdotLBlur = dot( s.NormalBlur, lightDir );
// Half-lambert lighting value based on blurred normals.
brdfUV.x = NdotLBlur * 0.5 + 0.5;
//Curvature amount. Multiplied by light's luminosity so brighter light = more scattering.
brdfUV.y = s.Curvature * dot( _LightColor0.rgb, fixed3(0.22, 0.707, 0.071 ) );
float3 brdf = tex2D( _BRDFTex, brdfUV ).rgb;
float m = atten; // Multiplier for spec and brdf.
#if !defined (SHADOWS_SCREEN) && !defined (SHADOWS_DEPTH) && !defined (SHADOWS_CUBE)
// If shadows are off, we need to reduce the brightness
// of the scattering on polys facing away from the light
// as it won't get killed off by shadow value.
// Same for the specular highlights.
m *= saturate( ( (NdotLBlur * 0.5 + 0.5) * 2.0) * 2.0 - 1.0);
#endif
fixed4 c;
c.rgb = (lerp(s.Albedo * saturate(NdotL) * atten, s.Albedo * brdf * m, s.Specular.b ) * _LightColor0.rgb + (spec * m * _LightColor0.rgb) ) * 2;
c.a = s.Curvature; // Output the curvature to the frame alpha, just as a debug.
return c;
}
ENDCG
}
FallBack "VertexLit"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment