Skip to content

Instantly share code, notes, and snippets.

@float3
Last active January 25, 2022 17:10
Show Gist options
  • Save float3/dbba6ab34b2605f47cfc56e748bf4ba5 to your computer and use it in GitHub Desktop.
Save float3/dbba6ab34b2605f47cfc56e748bf4ba5 to your computer and use it in GitHub Desktop.
Example of how to output parallax depth in the fragment shader for better clipping/shadow receiving/fog etc.
// you can output depth in the frag shader by returning a struct that contains a element with the SV_DEPTH semantic
// or by adding it as a extra out parameter, also with the SV_DEPTH semantic.
// the latter is useful if you want to if guard it
// example 1:
// float4 frag(v2f i, uint facing : SV_IsFrontFace
// #ifdef DEPTH_PARALLAX
// ,out float depth : SV_DEPTH
// #endif
// ) : SV_Target
// example 2:
// struct fragOut
// {
// float color : SV_Target;
// #ifdef DEPTH_PARALLAX
// float depth : SV_Depth;
// #endif
// };
// fragOut frag(v2f i, uint facing : SV_IsFrontFace)
// Be aware that outputting depth in frag disables early-Z
Shader "3/Parallax Depth"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Base Map", 2D) = "white" {}
_Parallax ("Height Scale", Range (0, 0.2)) = 0.02
_ParallaxMap ("Height Map", 2D) = "black" {}
[IntRange]_ParallaxSteps("Parallax Steps", Range(1,50)) = 25
_ParallaxOffset("Parallax Offset", Range(-1, 1)) = 0
}
SubShader
{
CGINCLUDE
struct fragOut
{
float4 color : SV_Target;
float depth : SV_Depth;
};
SamplerState sampler_ParallaxMap;
half4 _ParallaxMap_ST;
float _ParallaxSteps;
float _ParallaxOffset;
float _Parallax;
Texture2D _ParallaxMap;
float3 CalculateTangentViewDir(float3 tangentViewDir)
{
tangentViewDir = normalize(tangentViewDir);
tangentViewDir.xy /= (tangentViewDir.z + 0.42);
return tangentViewDir;
}
#define IN_VIEWDIR4PARALLAX(i) NormalizePerPixelNormal(half3(i.tangentToWorldAndPackedData[0].w,i.tangentToWorldAndPackedData[1].w,i.tangentToWorldAndPackedData[2].w))
#define IN_VIEWDIR4PARALLAX_FWDADD(i) NormalizePerPixelNormal(i.viewDirForParallax.xyz)
float3 ParallaxOffsetMultiStep(float surfaceHeight, float strength, float2 uv, float3 tangentViewDir)
{
float2 uvOffset = 0;
float stepSize = 1.0 / _ParallaxSteps;
float stepHeight = 1;
float2 uvDelta = tangentViewDir.xy * (stepSize * strength);
[unroll(50)]
for (int j = 1; j <= _ParallaxSteps && stepHeight > surfaceHeight; j++)
{
uvOffset -= uvDelta;
stepHeight -= stepSize;
surfaceHeight = _ParallaxMap.Sample(sampler_ParallaxMap, (uv + uvOffset)) + _ParallaxOffset;
}
[unroll(3)]
for (int k = 0; k < 3; k++)
{
uvDelta *= 0.5;
stepSize *= 0.5;
if (stepHeight < surfaceHeight)
{
uvOffset += uvDelta;
stepHeight += stepSize;
}
else
{
uvOffset -= uvDelta;
stepHeight -= stepSize;
}
surfaceHeight = _ParallaxMap.Sample(sampler_ParallaxMap, (uv + uvOffset)) + _ParallaxOffset;
}
return float3(uvOffset, surfaceHeight);
}
float3 ParallaxOffset(float3 viewDirForParallax, float2 parallaxUV)
{
viewDirForParallax = CalculateTangentViewDir(viewDirForParallax);
float h = _ParallaxMap.Sample(sampler_ParallaxMap, parallaxUV);
h = clamp(h, 0, 0.999);
float3 offset = ParallaxOffsetMultiStep(h, _Parallax, parallaxUV, viewDirForParallax);
return offset;
}
static SamplerState defaultSampler;
Texture2D _MainTex;
SamplerState sampler_MainTex;
half4 _MainTex_ST;
half4 _Color;
ENDCG
Pass
{
Tags {"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float4 tangent : TANGENT;
float3 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 uv : TEXuv;
float3 worldNormal : TEXCOORD1;
float4 worldPos : TEXCOORD2;
float3 viewDir : TEXCOORD3;
UNITY_FOG_COORDS(4)
UNITY_LIGHTING_COORDS(5, 6)
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert(appdata v)
{
v2f o = (v2f)0;
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv0.xy, _MainTex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
// TANGENTROTATION Macro
float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal.xyz);
//
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
UNITY_TRANSFER_FOG(o, o.pos);
UNITY_TRANSFER_LIGHTING(o, v.uv1);
o.uv.zw = v.uv1;
return o;
}
fragOut frag(v2f i)
{
fragOut o;
float3 offset = ParallaxOffset(i.viewDir, i.uv);
i.worldPos.xyz = i.worldPos + i.worldNormal * offset.z;
i.pos = UnityWorldToClipPos(i.worldPos);
float4 proj = mul(UNITY_MATRIX_VP, float4(i.worldPos.xyz, 1));
o.depth = proj.z / proj.w;
half4 mainTexture = _MainTex.Sample(sampler_MainTex, i.uv.xy + offset.xy);
mainTexture *= _Color;
half3 indirectDiffuse;
half3 light = 0;
#ifdef LIGHTMAP_ON
indirectDiffuse = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv.zw + offset.xy));
#else
indirectDiffuse = max(0, ShadeSH9(float4(i.worldNormal, 1)));
#endif
#ifdef USING_LIGHT_MULTI_COMPILE
float3 lightDirection = UnityWorldSpaceLightDir(i.worldPos);
half NoL = saturate(dot(i.worldNormal, lightDirection));
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos.xyz);
light = NoL * _LightColor0.rgb * atten;
#endif
half4 finalColor = half4(mainTexture.rgb * (indirectDiffuse + light), 1);
UNITY_APPLY_FOG(i.fogCoord, finalColor);
o.color = finalColor;
return o;
}
ENDCG
}
Pass
{
Tags {"LightMode" = "ForwardAdd"}
Blend One One, One One
Fog {Color (0,0,0,0)}
ZWrite Off
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#pragma multi_compile_fwdadd_fullshadows
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float4 tangent : TANGENT;
float3 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 uv : TEXuv;
float3 worldNormal : TEXCOORD1;
float4 worldPos : TEXCOORD2;
float3 viewDir : TEXCOORD3;
UNITY_FOG_COORDS(4)
UNITY_LIGHTING_COORDS(5, 6)
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert(appdata v)
{
v2f o = (v2f)0;
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv0.xy, _MainTex);
o.uv.zw = v.uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
// TANGENTROTATION Macro
float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal.xyz);
//
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
UNITY_TRANSFER_FOG(o, o.pos);
UNITY_TRANSFER_LIGHTING(o, v.uv1);
o.uv.zw = v.uv1;
return o;
}
fragOut frag(v2f i)
{
fragOut o;
float3 offset = ParallaxOffset(i.viewDir, i.uv);
i.worldPos.xyz = i.worldPos + i.worldNormal * offset.z;
i.pos = UnityWorldToClipPos(i.worldPos);
float4 proj = mul(UNITY_MATRIX_VP, float4(i.worldPos.xyz, 1));
o.depth = proj.z / proj.w;
half4 mainTexture = _MainTex.Sample(sampler_MainTex, i.uv.xy + offset.xy);
mainTexture *= _Color;
half3 indirectDiffuse;
half3 light = 0;
#ifdef LIGHTMAP_ON
indirectDiffuse = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv.zw + offset.xy));
#else
indirectDiffuse = max(0, ShadeSH9(float4(i.worldNormal, 1)));
#endif
#ifdef USING_LIGHT_MULTI_COMPILE
float3 lightDirection = UnityWorldSpaceLightDir(i.worldPos);
half NoL = saturate(dot(i.worldNormal, lightDirection));
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos.xyz);
light = NoL * _LightColor0.rgb * atten;
#endif
half4 finalColor = half4(mainTexture.rgb * (indirectDiffuse + light), 1);
UNITY_APPLY_FOG(i.fogCoord, finalColor);
o.color = finalColor;
return o;
}
ENDCG
}
Pass
{
Tags {"LightMode" = "ShadowCaster"}
ZWrite On ZTest LEqual Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 5.0
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float4 tangent : TANGENT;
float3 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 uv : TEXuv;
float3 worldNormal : TEXCOORD1;
float4 worldPos : TEXCOORD2;
float3 viewDir : TEXCOORD3;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert(appdata v)
{
v2f o;
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv0.xy, _MainTex);
o.uv.zw = v.uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
// TANGENTROTATION Macro
float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal.xyz);
//
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
fragOut frag(v2f i)
{
fragOut o;
float3 offset = ParallaxOffset(i.viewDir, i.uv);
i.worldPos.xyz = i.worldPos + i.worldNormal * offset.z;
float4 proj = mul(UNITY_MATRIX_VP, float4(i.worldPos.xyz, 1));
o.depth = proj.z / proj.w;
//SHADOWCASTERFRAGMENT Macro inlined
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
o.color = UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);
#else
o.color = 0;
#endif
//
return o;
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment