Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Forked from ForgeCreations/Microvolume.shader
Created May 16, 2026 12:01
Show Gist options
  • Select an option

  • Save unitycoder/ab03eaa7b0e6f24d8f1edf3166b485b2 to your computer and use it in GitHub Desktop.

Select an option

Save unitycoder/ab03eaa7b0e6f24d8f1edf3166b485b2 to your computer and use it in GitHub Desktop.
This is the current state of my Microvolume concept as a Unity Shader for URP. You're welcome to play around with it in any way you want.
Shader "Custom/Microvolume/LitURP"
{
Properties
{
[MainTexture]_BaseMap("Albedo", 2D) = "white" {}
[MainColor]_BaseColor("Albedo Color", Color) = (1, 1, 1, 1)
[Normal]_NormalMap("Normal Map", 2D) = "bump" {}
_NormalScale("Normal Scale", Range(0, 2)) = 1
_HeightMap("Height Map", 2D) = "black" {}
_HeightScale("Height Scale", Range(0, 2)) = 1
_HeightBias("Height Bias", Range(-1, 1)) = 0
_VertexDisplacement("Vertex Displacement", Range(0, 0.2)) = 0.02
_ParallaxStrength("Parallax Strength", Range(0, 0.2)) = 0.05
_MinSteps("Min Steps", Range(1, 32)) = 4
_MaxSteps("Max Steps", Range(1, 32)) = 16
_RefineSteps("Refine Steps", Range(0, 8)) = 4
_SilhouetteSoftness("Silhouette Softness", Range(0, 128)) = 12
_MicrovolumeSlopeContribution("Slope Contribution", Range(0, 1)) = 0.15
_MicrovolumeOcclusionContribution("Occlusion Contribution", Range(0, 1)) = 0.15
_MicrovolumeRoughnessContribution("Roughness Contribution", Range(0, 1)) = 0.05
_MicrovolumeDetailContribution("Detail Contribution", Range(0, 1)) = 0.05
_MicrovolumeConfidence("Confidence", Range(0, 2)) = 1
_SilhouetteStart("Silhouette Start", Range(0, 1)) = 0.55
_SilhouetteEnd("Silhouette End", Range(0, 1)) = 0.92
_MaxMicrovolumeOffset("Max Microvolume Offset", Range(0, 0.25)) = 0.08
_StretchSoftness("Stretch Softness", Range(0.1, 8)) = 2
_SilhouetteBoost("Silhouette Boost", Range(0, 4)) = 1
[Toggle(_MICROVOLUME_ALPHA_CLIP)] _AlphaClip("Alpha Clip", Float) = 0
_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5
[Toggle(_MICROVOLUME_OCCLUSION_MAP)] _UseOcclusionMap("Use Occlusion Map", Float) = 0
[NoScaleOffset]_OcclusionMap("Occlusion Map", 2D) = "white" {}
_OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1
[Toggle(_MICROVOLUME_ROUGHNESS_MAP)] _UseRoughnessMap("Use Roughness Map", Float) = 0
[NoScaleOffset]_RoughnessMap("Roughness Map", 2D) = "white" {}
_Roughness("Roughness", Range(0, 1)) = 0.5
[NoScaleOffset]_MetallicGlossMap("Metallic Map", 2D) = "black" {}
_Metallic("Metallic", Range(0, 1)) = 0
[Toggle(_MICROVOLUME_SPECULAR_WORKFLOW)] _UseSpecularWorkflow("Specular Workflow", Float) = 0
[NoScaleOffset]_SpecGlossMap("Specular Map", 2D) = "black" {}
_SpecColor("Specular Color", Color) = (0.04, 0.04, 0.04, 1)
_Smoothness("Smoothness", Range(0, 1)) = 0.5
[Toggle(_MICROVOLUME_EMISSION_MAP)] _UseEmissionMap("Use Emission Map", Float) = 0
[HDR]_EmissionColor("Emission Color", Color) = (0, 0, 0, 0)
[NoScaleOffset]_EmissionMap("Emission Map", 2D) = "black" {}
_ShadowSteps("Shadow Steps", Range(1, 24)) = 8
_ShadowSoftness("Shadow Softness", Range(0, 128)) = 12
_ShadowParallaxStrength("Shadow Parallax Strength", Range(0, 0.2)) = 0.04
_ShadowStrength("Shadow Strength", Range(0, 1)) = 1
_IndirectStrength("Indirect Strength", Range(0, 5)) = 1
[Enum(None,0, Height,1, FieldBias,2, Coverage,3, Confidence,4, Slope,5, SelfShadow,6, Silhouette,7, UVOffset,8, Stretch,9, Roughness,10)] _MicrovolumeDebugView("Microvolume Debug View", Float) = 0
_DebugOverlay("Debug Overlay", Range(0 ,1)) = 1
_DebugScale("Debug Scale", Range(0.1, 10)) = 1
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="TransparentCutout"
"Queue"="AlphaTest"
}
Cull Back
ZWrite On
Blend One Zero
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
float4 _BaseColor;
float4 _NormalMap_ST;
float _NormalScale;
float4 _HeightMap_ST;
float _HeightScale;
float _HeightBias;
float _VertexDisplacement;
float _ParallaxStrength;
float _MinSteps;
float _MaxSteps;
float _RefineSteps;
float _SilhouetteSoftness;
float _MicrovolumeSlopeContribution;
float _MicrovolumeOcclusionContribution;
float _MicrovolumeRoughnessContribution;
float _MicrovolumeDetailContribution;
float _MicrovolumeConfidence;
float _SilhouetteStart;
float _SilhouetteEnd;
float _MaxMicrovolumeOffset;
float _StretchSoftness;
float _SilhouetteBoost;
float _Cutoff;
float4 _OcclusionMap_ST;
float _OcclusionStrength;
float4 _RoughnessMap_ST;
float _Roughness;
float4 _MetallicGlossMap_ST;
float _Metallic;
float4 _SpecGlossMap_ST;
float4 _SpecColor;
float _Smoothness;
float4 _EmissionMap_ST;
float4 _EmissionColor;
float _ShadowSteps;
float _ShadowSoftness;
float _ShadowParallaxStrength;
float _ShadowStrength;
float _IndirectStrength;
float _MicrovolumeDebugView;
float _DebugOverlay;
float _DebugScale;
CBUFFER_END
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap);
TEXTURE2D(_HeightMap); SAMPLER(sampler_HeightMap);
TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap);
TEXTURE2D(_RoughnessMap); SAMPLER(sampler_RoughnessMap);
TEXTURE2D(_MetallicGlossMap); SAMPLER(sampler_MetallicGlossMap);
TEXTURE2D(_SpecGlossMap); SAMPLER(sampler_SpecGlossMap);
TEXTURE2D(_EmissionMap); SAMPLER(sampler_EmissionMap);
#pragma multi_compile_instancing
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma shader_feature_local_fragment _MICROVOLUME_ALPHA_CLIP
#pragma shader_feature_local_fragment _MICROVOLUME_SPECULAR_WORKFLOW
#pragma shader_feature_local_fragment _MICROVOLUME_ROUGHNESS_MAP
#pragma shader_feature_local_fragment _MICROVOLUME_OCCLUSION_MAP
#pragma shader_feature_local_fragment _MICROVOLUME_EMISSION_MAP
static const int MICROVOLUME_MAX_PARALLAX_STEPS = 32;
static const int MICROVOLUME_MAX_SHADOW_STEPS = 24;
static const int MICROVOLUME_MAX_REFINE_STEPS = 8;
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float3 tangentWS : TEXCOORD2;
float3 bitangentWS : TEXCOORD3;
float2 uv : TEXCOORD4;
float4 shadowCoord : TEXCOORD5;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
struct MicrovolumeHit
{
float2 uv;
float depth;
float coverage;
float confidence;
float silhouette;
float stretch;
bool hit;
};
float2 TransformUV(float2 uv, float4 st)
{
return uv * st.xy + st.zw;
}
float3 BlendNormals(float3 n1, float3 n2)
{
float3 t = float3(n1.xy + n2.xy, n1.z * n2.z - dot(n1.xy, n2.xy));
return normalize(t);
}
float SampleRoughnessValue(float2 uv)
{
float rough = _Roughness;
#if defined(_MICROVOLUME_ROUGHNESS_MAP)
rough *= SAMPLE_TEXTURE2D(_RoughnessMap, sampler_RoughnessMap, uv).r;
#endif
return saturate(rough);
}
float SampleOcclusionValue(float2 uv)
{
float ao = 1.0;
#if defined(_MICROVOLUME_OCCLUSION_MAP)
ao = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).r;
#endif
return lerp(1.0, ao, _OcclusionStrength);
}
float3 SampleNormalTS(float2 uv)
{
float3 n = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, uv), _NormalScale);
return normalize(n);
}
float3 SampleBaseColor(float2 uv)
{
float2 buv = TransformUV(uv, _BaseMap_ST);
float3 c = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, buv).rgb * _BaseColor.rgb;
return c;
}
float SampleAlpha(float2 uv)
{
float2 buv = TransformUV(uv, _BaseMap_ST);
return SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, buv).a * _BaseColor.a;
}
float SampleSmoothnessValue(float2 uv)
{
float smoothness = _Smoothness;
#if defined(_MICROVOLUME_SPECULAR_WORKFLOW)
smoothness *= SAMPLE_TEXTURE2D(_SpecGlossMap, sampler_SpecGlossMap, TransformUV(uv, _SpecGlossMap_ST)).a;
#else
smoothness *= SAMPLE_TEXTURE2D(_MetallicGlossMap, sampler_MetallicGlossMap, TransformUV(uv, _MetallicGlossMap_ST)).a;
#endif
return saturate(smoothness);
}
float3 SampleF0(float2 uv, float3 albedo, out float metallic)
{
#if defined(_MICROVOLUME_SPECULAR_WORKFLOW)
float3 spec = SAMPLE_TEXTURE2D(_SpecGlossMap, sampler_SpecGlossMap, TransformUV(uv, _SpecGlossMap_ST)).rgb * _SpecColor.rgb;
metallic = 0.0;
return saturate(spec);
#else
metallic = SAMPLE_TEXTURE2D(_MetallicGlossMap, sampler_MetallicGlossMap, TransformUV(uv, _MetallicGlossMap_ST)).r * _Metallic;
return saturate(lerp(0.04.xxx, albedo, metallic));
#endif
}
float3 SampleEmission(float2 uv)
{
#if defined(_MICROVOLUME_EMISSION_MAP)
return SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, TransformUV(uv, _EmissionMap_ST)).rgb * _EmissionColor.rgb;
#else
return _EmissionColor.rgb;
#endif
}
float MicrovolumeFieldBias(float2 uv)
{
float3 n = SampleNormalTS(TransformUV(uv, _NormalMap_ST));
float ao = SampleOcclusionValue(TransformUV(uv, _OcclusionMap_ST));
float rough = SampleRoughnessValue(TransformUV(uv, _RoughnessMap_ST));
float slope = saturate(1.0 - n.z) * _MicrovolumeSlopeContribution;
float cavity = (1.0 - ao) * _MicrovolumeOcclusionContribution;
float roughTerm = rough * _MicrovolumeRoughnessContribution;
return slope + cavity + roughTerm;
}
float MicrovolumeFieldHeight(float2 uv, float fieldBias)
{
float h = SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, TransformUV(uv, _HeightMap_ST)).r;
return saturate(h * _HeightScale + _HeightBias + fieldBias);
}
float MicrovolumeSupportRadius(float2 uv)
{
float3 n = SampleNormalTS(TransformUV(uv, _NormalMap_ST));
float ao = SampleOcclusionValue(TransformUV(uv, _OcclusionMap_ST));
float rough = SampleRoughnessValue(TransformUV(uv, _RoughnessMap_ST));
float slope = saturate(1.0 - n.z);
float sharpness = saturate(1.0 - rough);
float cavity = 1.0 - ao;
float radius = _MaxMicrovolumeOffset;
radius *= lerp(0.35, 1.0, slope);
radius *= lerp(0.75, 1.0, sharpness);
radius *= lerp(0.70, 1.0, 1.0 - cavity);
return max(radius, 0.0005);
}
float MicrovolumeSilhouetteFactor(float ndv, float confidence, float stretch)
{
float grazing = smoothstep(_SilhouetteStart, _SilhouetteEnd, 1.0 - ndv);
float excess = saturate(stretch);
return saturate(grazing * confidence * (1.0 + excess * _SilhouetteBoost));
}
float MicrovolumeConservativeCoverage(float2 uv)
{
float alpha = SampleAlpha(uv);
float fieldBias = MicrovolumeFieldBias(uv);
float h = MicrovolumeFieldHeight(uv, fieldBias);
float ao = SampleOcclusionValue(TransformUV(uv, _OcclusionMap_ST));
float coverage = alpha;
coverage *= lerp(0.82, 1.0, h);
coverage *= lerp(0.88, 1.0, ao);
return saturate(coverage);
}
MicrovolumeHit SolveMicrovolume(float2 uv, float3 dirTS)
{
MicrovolumeHit r;
dirTS = normalize(dirTS);
float vz = max(abs(dirTS.z), 0.05);
float ndv = saturate(vz);
float fieldBias = MicrovolumeFieldBias(uv);
float supportRadius = MicrovolumeSupportRadius(uv);
int steps = clamp((int)round(lerp(_MaxSteps, _MinSteps, ndv)), 1, MICROVOLUME_MAX_PARALLAX_STEPS);
int refineSteps = clamp((int)round(_RefineSteps), 0, MICROVOLUME_MAX_REFINE_STEPS);
float parallaxScale = min(_ParallaxStrength, supportRadius);
float2 stepUV = -dirTS.xy / vz * (parallaxScale / (float)steps);
float layerDepth = 1.0;
float stepDepth = 1.0 / (float)steps;
float2 currentUV = uv;
float currentHeight = MicrovolumeFieldHeight(currentUV, fieldBias);
float2 prevUV = currentUV;
float prevDepth = layerDepth;
float prevHeight = currentHeight;
bool hit = false;
[loop]
for (int i = 0; i < MICROVOLUME_MAX_PARALLAX_STEPS; i++)
{
if (i >= steps)
break;
prevUV = currentUV;
prevDepth = layerDepth;
prevHeight = currentHeight;
currentUV += stepUV;
layerDepth -= stepDepth;
currentHeight = MicrovolumeFieldHeight(currentUV, fieldBias);
if (layerDepth <= currentHeight)
{
hit = true;
break;
}
}
if (hit)
{
float2 aUV = prevUV;
float2 bUV = currentUV;
float aD = prevDepth;
float bD = layerDepth;
[unroll]
for (int j = 0; j < MICROVOLUME_MAX_REFINE_STEPS; j++)
{
if (j >= refineSteps)
break;
float2 mUV = 0.5 * (aUV + bUV);
float mD = 0.5 * (aD + bD);
float mH = MicrovolumeFieldHeight(mUV, fieldBias);
if (mD <= mH)
{
bUV = mUV;
bD = mD;
}
else
{
aUV = mUV;
aD = mD;
}
}
currentUV = bUV;
layerDepth = bD;
currentHeight = MicrovolumeFieldHeight(currentUV, fieldBias);
}
float2 deltaUV = currentUV - uv;
float offsetLen = length(deltaUV);
float stretch = saturate(offsetLen / max(supportRadius, 0.0005));
if (offsetLen > supportRadius)
{
float2 dir = deltaUV / max(offsetLen, 0.00001);
currentUV = uv + dir * supportRadius;
}
float coverage = hit ? saturate(1.0 - abs(currentHeight - layerDepth) * _SilhouetteSoftness) : 1.0;
coverage *= saturate(1.0 - max(0.0, stretch - 1.0));
float confidence = saturate(_MicrovolumeConfidence * lerp(0.65, 1.0, ndv));
float silhouette = MicrovolumeSilhouetteFactor(ndv, confidence, stretch);
r.uv = currentUV;
r.depth = layerDepth;
r.coverage = coverage;
r.confidence = confidence;
r.silhouette = silhouette;
r.stretch = stretch;
r.hit = hit;
return r;
}
float MicrovolumeSelfShadow(float2 uv, float3 lightDirTS, float startDepth)
{
lightDirTS = normalize(lightDirTS);
float vz = max(abs(lightDirTS.z), 0.05);
float fieldBias = MicrovolumeFieldBias(uv);
int steps = clamp((int)round(_ShadowSteps), 1, MICROVOLUME_MAX_SHADOW_STEPS);
float2 stepUV = -lightDirTS.xy / vz * (_ShadowParallaxStrength / (float)steps);
float layerDepth = startDepth;
float stepDepth = (1.0 - startDepth) / (float)steps;
float shadow = 1.0;
float2 currentUV = uv;
[loop]
for (int i = 0; i < MICROVOLUME_MAX_SHADOW_STEPS; i++)
{
if (i >= steps)
break;
currentUV += stepUV;
layerDepth += stepDepth;
float h = MicrovolumeFieldHeight(currentUV, fieldBias);
float pen = h - layerDepth;
shadow = min(shadow, saturate(1.0 - pen * _ShadowSoftness));
if (shadow <= 0.01)
break;
}
return saturate(shadow);
}
float MVD_GGX(float NdotH, float roughness)
{
float a = max(roughness * roughness, 0.002);
float a2 = a * a;
float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
return a2 / max(PI * d * d, 1e-4);
}
float G_Smith(float NdotV, float NdotL, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) * 0.125;
float gv = NdotV / max(NdotV * (1.0 - k) + k, 1e-4);
float gl = NdotL / max(NdotL * (1.0 - k) + k, 1e-4);
return gv * gl;
}
float3 MVF_Schlick(float3 F0, float cosTheta)
{
float f = pow(1.0 - saturate(cosTheta), 5.0);
return F0 + (1.0 - F0) * f;
}
float3 EvaluateBRDF(float3 albedo, float3 F0, float roughness, float3 N, float3 V, float3 L)
{
float3 H = normalize(V + L);
float NdotL = saturate(dot(N, L));
float NdotV = saturate(dot(N, V));
float NdotH = saturate(dot(N, H));
float VdotH = saturate(dot(V, H));
float D = MVD_GGX(NdotH, roughness);
float G = G_Smith(NdotV, NdotL, roughness);
float3 F = MVF_Schlick(F0, VdotH);
float3 spec = (D * G * F) / max(4.0 * NdotL * NdotV, 1e-4);
float3 diff = (1.0 - F) * albedo * (1.0 / PI);
return (diff + spec) * NdotL;
}
float3 DebugHeat(float t)
{
t = saturate(t);
return saturate(float3(
saturate(1.5 - abs(2.0 * t - 1.0)),
saturate(1.5 - abs(2.0 * t - 0.5)),
saturate(1.5 - abs(2.0 * t - 0.0))
));
}
float3 DebugBlueRed(float t)
{
t = saturate(t);
return saturate(float3(t, 0.15, 1.0 - t));
}
float3 DebugGreenRed(float t)
{
t = saturate(t);
return saturate(float3(1.0 - t, t, 0.1));
}
float3 DebugMicrovolume(Varyings IN, MicrovolumeHit mv, float2 uv, float3 viewDirWS, float3 viewDirTS)
{
int mode = (int)round(_MicrovolumeDebugView);
float h = MicrovolumeFieldHeight(uv, MicrovolumeFieldBias(uv));
float rough = SampleRoughnessValue(TransformUV(uv, _RoughnessMap_ST));
float slope = saturate(1.0 - SampleNormalTS(TransformUV(uv, _NormalMap_ST)).z);
//float silhouette = saturate((1.0 - abs(viewDirTS.z)) * mv.coverage * mv.confidence);
float shadow = MicrovolumeSelfShadow(uv, float3(0, 0, 1), mv.depth);
float uvOffset = saturate(length(uv - IN.uv) * _DebugScale);
if (mode == 1) return DebugHeat(h);
if (mode == 2) return DebugBlueRed(MicrovolumeFieldBias(uv));
if (mode == 3) return DebugGreenRed(mv.coverage);
if (mode == 4) return DebugGreenRed(mv.confidence);
if (mode == 5) return DebugHeat(slope);
if (mode == 6) return DebugHeat(shadow);
if (mode == 7) return DebugHeat(mv.silhouette);
if (mode == 8) return DebugHeat(uvOffset);
if (mode == 9) return DebugHeat(mv.stretch);
if (mode == 10) return DebugHeat(rough);
return 0;
}
Varyings vert(Attributes IN)
{
Varyings OUT;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
VertexNormalInputs normalInputs = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
float2 heightUV = TransformUV(IN.uv, _HeightMap_ST);
float coarseHeight = SAMPLE_TEXTURE2D_LOD(_HeightMap, sampler_HeightMap, heightUV, 0).r;
float coarseOffset = ((coarseHeight * _HeightScale + _HeightBias) - 0.5) * _VertexDisplacement;
positionWS += normalInputs.normalWS * coarseOffset;
OUT.positionWS = positionWS;
OUT.normalWS = normalize(normalInputs.normalWS);
OUT.tangentWS = normalize(normalInputs.tangentWS);
OUT.bitangentWS = normalize(normalInputs.bitangentWS);
OUT.uv = IN.uv;
OUT.positionCS = TransformWorldToHClip(positionWS);
OUT.shadowCoord = TransformWorldToShadowCoord(positionWS);
return OUT;
}
half4 fragForward(Varyings IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
float2 baseUV = IN.uv;
float3 viewDirWS = GetWorldSpaceNormalizeViewDir(IN.positionWS);
float3 viewDirTS = float3(
dot(viewDirWS, IN.tangentWS),
dot(viewDirWS, IN.bitangentWS),
dot(viewDirWS, IN.normalWS)
);
MicrovolumeHit mv = SolveMicrovolume(baseUV, viewDirTS);
float silhouetteWeight = mv.silhouette;
mv.coverage = saturate(lerp(mv.coverage, 1.0, silhouetteWeight));
float2 uv = mv.uv;
if (_MicrovolumeDebugView > 0.5)
{
float3 debugColor = DebugMicrovolume(IN, mv, uv, viewDirWS, viewDirTS);
return half4(debugColor, 1.0);
}
float alpha = SampleAlpha(uv);
alpha *= mv.coverage;
#if defined(_MICROVOLUME_ALPHA_CLIP)
clip(alpha - _Cutoff);
#endif
float3 albedo = SampleBaseColor(uv);
float roughness = 1.0 - SampleSmoothnessValue(uv);
#if defined(_MICROVOLUME_ROUGHNESS_MAP)
roughness = saturate(roughness * SampleRoughnessValue(TransformUV(uv, _RoughnessMap_ST)));
#endif
float metallic = 0.0;
float3 F0 = SampleF0(uv, albedo, metallic);
float3 detailNormalTS = SampleNormalTS(TransformUV(uv, _NormalMap_ST));
float3 N = normalize(
IN.tangentWS * detailNormalTS.x +
IN.bitangentWS * detailNormalTS.y +
IN.normalWS * detailNormalTS.z
);
float3 V = normalize(viewDirWS);
float ao = SampleOcclusionValue(TransformUV(uv, _OcclusionMap_ST));
float3 color = 0.0;
Light mainLight = GetMainLight(IN.shadowCoord);
{
float3 L = normalize(mainLight.direction);
float3 lightDirTS = float3(
dot(L, IN.tangentWS),
dot(L, IN.bitangentWS),
dot(L, IN.normalWS)
);
float microShadow = MicrovolumeSelfShadow(uv, lightDirTS, mv.depth) * _ShadowStrength;
float visibility = mainLight.shadowAttenuation * lerp(1.0, microShadow, 1.0);
color += EvaluateBRDF(albedo, F0, roughness, N, V, L) * mainLight.color * mainLight.distanceAttenuation * visibility;
}
uint additionalLightsCount = GetAdditionalLightsCount();
for (uint i = 0u; i < additionalLightsCount; ++i)
{
Light light = GetAdditionalLight(i, IN.positionWS);
float3 L = normalize(light.direction);
float3 lightDirTS = float3(
dot(L, IN.tangentWS),
dot(L, IN.bitangentWS),
dot(L, IN.normalWS)
);
float additionalShadow = 1.0;
#if defined(_ADDITIONAL_LIGHT_SHADOWS)
additionalShadow = AdditionalLightRealtimeShadow(i, IN.positionWS);
#endif
float microShadow = MicrovolumeSelfShadow(uv, lightDirTS, mv.depth);
float visibility = additionalShadow * microShadow;
color += EvaluateBRDF(albedo, F0, roughness, N, V, L) * light.color * light.distanceAttenuation * visibility;
}
float3 ambient = SampleSH(N) * albedo * ao * _IndirectStrength;
float3 emission = SampleEmission(uv);
color += ambient + emission;
return half4(saturate(color), alpha);
}
half4 fragShadow(Varyings IN) : SV_Target
{
float coverage = MicrovolumeConservativeCoverage(IN.uv);
#if defined(_MICROVOLUME_ALPHA_CLIP)
clip(coverage - _Cutoff);
#else
clip(coverage - 0.001);
#endif
return 0;
}
half4 fragDepth(Varyings IN) : SV_Target
{
float coverage = MicrovolumeConservativeCoverage(IN.uv);
#if defined(_MICROVOLUME_ALPHA_CLIP)
clip(coverage - _Cutoff);
#else
clip(coverage - 0.001);
#endif
return 0;
}
half4 fragDepthNormals(Varyings IN) : SV_Target
{
float coverage = MicrovolumeConservativeCoverage(IN.uv);
#if defined(_MICROVOLUME_ALPHA_CLIP)
clip(coverage - _Cutoff);
#else
clip(coverage - 0.001);
#endif
float3 nTS = SampleNormalTS(TransformUV(IN.uv, _NormalMap_ST));
float3 nWS = normalize(
IN.tangentWS * nTS.x +
IN.bitangentWS * nTS.y +
IN.normalWS * nTS.z
);
return half4(nWS * 0.5 + 0.5, 1.0);
}
ENDHLSL
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForwardOnly" }
HLSLPROGRAM
#pragma vertex vert
#pragma fragment fragForward
ENDHLSL
}
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
ZWrite On
ColorMask 0
HLSLPROGRAM
#pragma vertex vert
#pragma fragment fragShadow
ENDHLSL
}
Pass
{
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }
ZWrite On
ColorMask 0
HLSLPROGRAM
#pragma vertex vert
#pragma fragment fragDepth
ENDHLSL
}
Pass
{
Name "DepthNormalsOnly"
Tags { "LightMode" = "DepthNormalsOnly" }
ZWrite On
HLSLPROGRAM
#pragma vertex vert
#pragma fragment fragDepthNormals
ENDHLSL
}
}
FallBack Off
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment