Last active
January 25, 2022 17:10
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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