Last active
May 16, 2026 12:01
-
-
Save ForgeCreations/ae24ce68f1370c5efd24202cebe54b14 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.
This file contains hidden or 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
| 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