Shader (PictureTessellationLit): - Lighting parity with URP Lit: lightmaps, shadowmask/mixed, APV, DBuffer decals, vertex additional lights, rendering layers, fragment fog - UniversalGBuffer pass (with tessellation + graded albedo) so HTrace WSGI composes correct GI instead of a cyan veil over pictures - Skip GPU tessellation entirely when strength/displacement is 0 - Inspector ranges matched to builder (-3..3 bump, -10..10 tessellation) Builder: - Frame Thickness & Padding are now absolute meters (size-independent) instead of fractions of the picture size - Tooltip updates for the new default/range semantics Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
648 lines
29 KiB
Plaintext
648 lines
29 KiB
Plaintext
// Forward-only URP PBR shader with hardware tessellation + height-map displacement and an occlusion
|
|
// map, built for the PictureFrameBuilder picture surface (works on any mesh with sane UVs).
|
|
//
|
|
// Deferred+ compatible via the ComplexLit pattern (same as PlayerCamoLit): "UniversalMaterialType" =
|
|
// "ComplexLit" stencils these pixels out of the deferred lighting and URP shades them in the
|
|
// UniversalForwardOnly pass instead - the only way a custom vertex pipeline (tessellation) can run
|
|
// on the deferred path. ALL depth-relevant passes (ShadowCaster / DepthOnly / DepthNormalsOnly) run
|
|
// the same hull/domain displacement so shadows, SSAO and depth match the displaced surface.
|
|
//
|
|
// Displacement = (height - 0.5) * _Displacement * _TessellationStrength along the vertex normal;
|
|
// _Displacement is the authored amplitude in meters, _TessellationStrength is the runtime knob the
|
|
// PictureFrameBuilder drives per frame (like _BumpScale / _OcclusionStrength).
|
|
Shader "Chameleon/PictureTessellationLit"
|
|
{
|
|
Properties
|
|
{
|
|
_BaseMap("Albedo", 2D) = "white" {}
|
|
_BaseColor("Color", Color) = (1, 1, 1, 1)
|
|
_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
|
|
_Metallic("Metallic", Range(0.0, 1.0)) = 0.0
|
|
[Normal] _BumpMap("Normal Map", 2D) = "bump" {}
|
|
_BumpScale("Normal Strength", Range(-3.0, 3.0)) = 1.0
|
|
_HeightMap("Height Map", 2D) = "gray" {}
|
|
_Displacement("Displacement (m)", Range(0.0, 0.2)) = 0.02
|
|
_TessellationStrength("Tessellation Strength", Range(-10.0, 10.0)) = 1.0
|
|
_TessellationFactor("Tessellation Factor", Range(1.0, 32.0)) = 8.0
|
|
_OcclusionMap("Occlusion Map", 2D) = "white" {}
|
|
_OcclusionStrength("Occlusion Strength", Range(0.0, 3.0)) = 1.0
|
|
_OcclusionContrast("Occlusion Contrast", Range(-1.0, 3.0)) = 0.0
|
|
_Brightness("Brightness", Range(0.0, 3.0)) = 1.0
|
|
}
|
|
|
|
SubShader
|
|
{
|
|
Tags
|
|
{
|
|
"RenderType" = "Opaque"
|
|
"RenderPipeline" = "UniversalPipeline"
|
|
"UniversalMaterialType" = "ComplexLit"
|
|
"Queue" = "Geometry"
|
|
}
|
|
LOD 300
|
|
|
|
HLSLINCLUDE
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
|
|
|
TEXTURE2D(_HeightMap); SAMPLER(sampler_HeightMap);
|
|
|
|
CBUFFER_START(UnityPerMaterial)
|
|
float4 _BaseMap_ST;
|
|
half4 _BaseColor;
|
|
half _Smoothness;
|
|
half _Metallic;
|
|
half _BumpScale;
|
|
half _Displacement;
|
|
half _TessellationStrength;
|
|
half _TessellationFactor;
|
|
half _OcclusionStrength;
|
|
half _OcclusionContrast;
|
|
half _Brightness;
|
|
CBUFFER_END
|
|
|
|
struct Attributes
|
|
{
|
|
float4 positionOS : POSITION;
|
|
float3 normalOS : NORMAL;
|
|
float4 tangentOS : TANGENT;
|
|
float2 uv : TEXCOORD0;
|
|
float2 staticLightmapUV : TEXCOORD1;
|
|
float2 dynamicLightmapUV : TEXCOORD2;
|
|
};
|
|
|
|
// Hull/domain control point: everything stays in object space until the domain stage.
|
|
struct ControlPoint
|
|
{
|
|
float4 positionOS : INTERNALTESSPOS;
|
|
float3 normalOS : NORMAL;
|
|
float4 tangentOS : TANGENT;
|
|
float2 uv : TEXCOORD0;
|
|
float2 staticLightmapUV : TEXCOORD1;
|
|
float2 dynamicLightmapUV : TEXCOORD2;
|
|
};
|
|
|
|
struct TessFactors
|
|
{
|
|
float edge[3] : SV_TessFactor;
|
|
float inside : SV_InsideTessFactor;
|
|
};
|
|
|
|
ControlPoint TessVertex(Attributes input)
|
|
{
|
|
ControlPoint p;
|
|
p.positionOS = input.positionOS;
|
|
p.normalOS = input.normalOS;
|
|
p.tangentOS = input.tangentOS;
|
|
p.uv = input.uv;
|
|
p.staticLightmapUV = input.staticLightmapUV;
|
|
p.dynamicLightmapUV = input.dynamicLightmapUV;
|
|
return p;
|
|
}
|
|
|
|
TessFactors PatchConstants(InputPatch<ControlPoint, 3> patch)
|
|
{
|
|
TessFactors f;
|
|
// No displacement -> no reason to subdivide (keeps the wireframe a plain grid).
|
|
half factor = (_TessellationStrength == 0.0h || _Displacement == 0.0h) ? 1.0h : _TessellationFactor;
|
|
f.edge[0] = factor;
|
|
f.edge[1] = factor;
|
|
f.edge[2] = factor;
|
|
f.inside = factor;
|
|
return f;
|
|
}
|
|
|
|
[domain("tri")]
|
|
[outputcontrolpoints(3)]
|
|
[outputtopology("triangle_cw")]
|
|
[partitioning("fractional_odd")]
|
|
[patchconstantfunc("PatchConstants")]
|
|
ControlPoint TessHull(InputPatch<ControlPoint, 3> patch, uint id : SV_OutputControlPointID)
|
|
{
|
|
return patch[id];
|
|
}
|
|
|
|
#define TESS_INTERPOLATE(field) (patch[0].field * bary.x + patch[1].field * bary.y + patch[2].field * bary.z)
|
|
|
|
// Barycentric interpolation + height displacement along the vertex normal; each pass turns
|
|
// the resulting object-space vertex into its own varyings in its domain function.
|
|
Attributes TessInterpolate(OutputPatch<ControlPoint, 3> patch, float3 bary)
|
|
{
|
|
Attributes v;
|
|
v.positionOS = TESS_INTERPOLATE(positionOS);
|
|
v.normalOS = normalize(TESS_INTERPOLATE(normalOS));
|
|
v.tangentOS = TESS_INTERPOLATE(tangentOS);
|
|
v.uv = TESS_INTERPOLATE(uv);
|
|
v.staticLightmapUV = TESS_INTERPOLATE(staticLightmapUV);
|
|
v.dynamicLightmapUV = TESS_INTERPOLATE(dynamicLightmapUV);
|
|
float height = SAMPLE_TEXTURE2D_LOD(_HeightMap, sampler_HeightMap, TRANSFORM_TEX(v.uv, _BaseMap), 0).r;
|
|
v.positionOS.xyz += v.normalOS * ((height - 0.5) * _Displacement * _TessellationStrength);
|
|
return v;
|
|
}
|
|
ENDHLSL
|
|
|
|
Pass
|
|
{
|
|
Name "ForwardLit"
|
|
Tags { "LightMode" = "UniversalForwardOnly" }
|
|
|
|
ZWrite On
|
|
Cull Back
|
|
|
|
HLSLPROGRAM
|
|
#pragma target 4.6
|
|
#pragma vertex TessVertex
|
|
#pragma hull TessHull
|
|
#pragma domain ForwardDomain
|
|
#pragma fragment ForwardFragment
|
|
|
|
// Keyword set mirrored from URP Lit.shader's ForwardLit pass (Universal Pipeline +
|
|
// Unity defined keywords) so every lighting feature the standard Lit supports works
|
|
// here too: lightmaps, shadowmask/mixed lighting, APV probe volumes, decals (DBuffer),
|
|
// vertex additional lights, rendering-layer output, reflection probe rotation.
|
|
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
|
|
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
|
|
#pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX
|
|
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
|
|
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
|
|
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
|
|
#pragma multi_compile_fragment _ _REFLECTION_PROBE_ATLAS
|
|
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
|
|
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION
|
|
#pragma multi_compile_fragment _ _SCREEN_SPACE_IRRADIANCE
|
|
#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
|
|
#pragma multi_compile_fragment _ _LIGHT_COOKIES
|
|
#pragma multi_compile _ _LIGHT_LAYERS
|
|
#pragma multi_compile _ _CLUSTER_LIGHT_LOOP
|
|
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
|
|
|
|
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
|
|
#pragma multi_compile _ SHADOWS_SHADOWMASK
|
|
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
|
|
#pragma multi_compile _ LIGHTMAP_ON
|
|
#pragma multi_compile_fragment _ LIGHTMAP_BICUBIC_SAMPLING
|
|
#pragma multi_compile_fragment _ REFLECTION_PROBE_ROTATION
|
|
#pragma multi_compile _ DYNAMICLIGHTMAP_ON
|
|
#pragma multi_compile _ USE_LEGACY_LIGHTMAPS
|
|
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Fog.hlsl"
|
|
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl"
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"
|
|
|
|
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
|
|
TEXTURE2D(_BumpMap); SAMPLER(sampler_BumpMap);
|
|
TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap);
|
|
|
|
struct Varyings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
float2 uv : TEXCOORD0;
|
|
float3 positionWS : TEXCOORD1;
|
|
float3 normalWS : TEXCOORD2;
|
|
float4 tangentWS : TEXCOORD3; // w = bitangent sign
|
|
#ifdef _ADDITIONAL_LIGHTS_VERTEX
|
|
half4 fogFactorAndVertexLight : TEXCOORD4; // x: fog, yzw: vertex light
|
|
#else
|
|
half fogFactor : TEXCOORD4;
|
|
#endif
|
|
DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 5);
|
|
#ifdef DYNAMICLIGHTMAP_ON
|
|
float2 dynamicLightmapUV : TEXCOORD6;
|
|
#endif
|
|
#ifdef USE_APV_PROBE_OCCLUSION
|
|
float4 probeOcclusion : TEXCOORD7;
|
|
#endif
|
|
};
|
|
|
|
[domain("tri")]
|
|
Varyings ForwardDomain(TessFactors factors, OutputPatch<ControlPoint, 3> patch, float3 bary : SV_DomainLocation)
|
|
{
|
|
Attributes v = TessInterpolate(patch, bary);
|
|
VertexPositionInputs pos = GetVertexPositionInputs(v.positionOS.xyz);
|
|
VertexNormalInputs nrm = GetVertexNormalInputs(v.normalOS, v.tangentOS);
|
|
|
|
Varyings o = (Varyings)0;
|
|
o.positionCS = pos.positionCS;
|
|
o.positionWS = pos.positionWS;
|
|
o.normalWS = nrm.normalWS;
|
|
o.tangentWS = float4(nrm.tangentWS, v.tangentOS.w * GetOddNegativeScale());
|
|
o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
|
|
|
|
half fogFactor = 0;
|
|
#if !defined(_FOG_FRAGMENT)
|
|
fogFactor = ComputeFogFactor(pos.positionCS.z);
|
|
#endif
|
|
#ifdef _ADDITIONAL_LIGHTS_VERTEX
|
|
o.fogFactorAndVertexLight = half4(fogFactor, VertexLighting(pos.positionWS, nrm.normalWS));
|
|
#else
|
|
o.fogFactor = fogFactor;
|
|
#endif
|
|
|
|
OUTPUT_LIGHTMAP_UV(v.staticLightmapUV, unity_LightmapST, o.staticLightmapUV);
|
|
#ifdef DYNAMICLIGHTMAP_ON
|
|
o.dynamicLightmapUV = v.dynamicLightmapUV * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
|
|
#endif
|
|
OUTPUT_SH4(pos.positionWS, o.normalWS, GetWorldSpaceNormalizeViewDir(pos.positionWS), o.vertexSH, o.probeOcclusion);
|
|
return o;
|
|
}
|
|
|
|
void ForwardFragment(
|
|
Varyings input
|
|
, out half4 outColor : SV_Target0
|
|
#ifdef _WRITE_RENDERING_LAYERS
|
|
, out uint outRenderingLayers : SV_Target1
|
|
#endif
|
|
)
|
|
{
|
|
half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;
|
|
// Height-weighted brightness: raised areas get the full multiplier, deep areas stay
|
|
// untouched (height 0.5 = mid-gray default when no map is bound -> half effect).
|
|
half heightBr = SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, input.uv).r;
|
|
albedo.rgb *= lerp(1.0h, _Brightness, heightBr);
|
|
half3 normalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_BumpMap, sampler_BumpMap, input.uv), _BumpScale);
|
|
half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, input.uv).g;
|
|
// Contrast around mid-gray: 0 = as sampled, -1 = flat (no occlusion detail), 3 = 4x.
|
|
occ = saturate((occ - 0.5h) * (1.0h + _OcclusionContrast) + 0.5h);
|
|
|
|
float sgn = input.tangentWS.w;
|
|
float3 bitangentWS = sgn * cross(input.normalWS, input.tangentWS.xyz);
|
|
half3x3 tbn = half3x3(input.tangentWS.xyz, bitangentWS, input.normalWS);
|
|
|
|
InputData inputData = (InputData)0;
|
|
inputData.positionWS = input.positionWS;
|
|
inputData.positionCS = input.positionCS;
|
|
inputData.normalWS = NormalizeNormalPerPixel(TransformTangentToWorld(normalTS, tbn));
|
|
inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
|
|
inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
|
|
#if defined(_MAIN_LIGHT_SHADOWS_SCREEN)
|
|
inputData.shadowCoord = float4(inputData.normalizedScreenSpaceUV, 0, 1);
|
|
#else
|
|
inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS);
|
|
#endif
|
|
#ifdef _ADDITIONAL_LIGHTS_VERTEX
|
|
inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactorAndVertexLight.x);
|
|
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
|
|
#else
|
|
inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactor);
|
|
#endif
|
|
|
|
// Baked GI exactly like LitForwardPass.InitializeBakedGIData: lightmaps (static/
|
|
// dynamic), APV probe volumes, screen-space irradiance, shadowmask - else SH.
|
|
#if defined(_SCREEN_SPACE_IRRADIANCE)
|
|
inputData.bakedGI = SAMPLE_GI(_ScreenSpaceIrradiance, input.positionCS.xy);
|
|
#elif defined(DYNAMICLIGHTMAP_ON)
|
|
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.dynamicLightmapUV, input.vertexSH, inputData.normalWS);
|
|
inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
|
|
#elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2))
|
|
inputData.bakedGI = SAMPLE_GI(input.vertexSH,
|
|
GetAbsolutePositionWS(inputData.positionWS),
|
|
inputData.normalWS,
|
|
inputData.viewDirectionWS,
|
|
input.positionCS.xy,
|
|
input.probeOcclusion,
|
|
inputData.shadowMask);
|
|
#else
|
|
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.vertexSH, inputData.normalWS);
|
|
inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
|
|
#endif
|
|
|
|
SurfaceData surfaceData = (SurfaceData)0;
|
|
surfaceData.albedo = albedo.rgb;
|
|
surfaceData.alpha = 1;
|
|
surfaceData.metallic = _Metallic;
|
|
surfaceData.smoothness = _Smoothness;
|
|
surfaceData.normalTS = normalTS;
|
|
// Strength > 1 keeps darkening past the authored map (saturate stops negatives).
|
|
surfaceData.occlusion = saturate(LerpWhiteTo(occ, _OcclusionStrength));
|
|
|
|
#if defined(_DBUFFER)
|
|
ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData);
|
|
#endif
|
|
|
|
half4 color = UniversalFragmentPBR(inputData, surfaceData);
|
|
color.rgb = MixFog(color.rgb, inputData.fogCoord);
|
|
outColor = color;
|
|
#ifdef _WRITE_RENDERING_LAYERS
|
|
outRenderingLayers = EncodeMeshRenderingLayer();
|
|
#endif
|
|
}
|
|
ENDHLSL
|
|
}
|
|
|
|
Pass
|
|
{
|
|
Name "ShadowCaster"
|
|
Tags { "LightMode" = "ShadowCaster" }
|
|
|
|
ZWrite On
|
|
ZTest LEqual
|
|
ColorMask 0
|
|
Cull Back
|
|
|
|
HLSLPROGRAM
|
|
#pragma target 4.6
|
|
#pragma vertex TessVertex
|
|
#pragma hull TessHull
|
|
#pragma domain ShadowDomain
|
|
#pragma fragment ShadowFragment
|
|
|
|
// NOT multi_compile_vertex: the keyword is consumed in the DOMAIN stage here.
|
|
#pragma multi_compile _ _CASTING_PUNCTUAL_LIGHT_SHADOW
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
|
|
|
|
float3 _LightDirection;
|
|
float3 _LightPosition;
|
|
|
|
struct ShadowVaryings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
};
|
|
|
|
[domain("tri")]
|
|
ShadowVaryings ShadowDomain(TessFactors factors, OutputPatch<ControlPoint, 3> patch, float3 bary : SV_DomainLocation)
|
|
{
|
|
Attributes v = TessInterpolate(patch, bary);
|
|
float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
|
|
float3 normalWS = TransformObjectToWorldNormal(v.normalOS);
|
|
#if defined(_CASTING_PUNCTUAL_LIGHT_SHADOW)
|
|
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
|
|
#else
|
|
float3 lightDirectionWS = _LightDirection;
|
|
#endif
|
|
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
|
|
#if UNITY_REVERSED_Z
|
|
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
|
|
#else
|
|
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
|
|
#endif
|
|
ShadowVaryings o;
|
|
o.positionCS = positionCS;
|
|
return o;
|
|
}
|
|
|
|
half4 ShadowFragment() : SV_Target
|
|
{
|
|
return 0;
|
|
}
|
|
ENDHLSL
|
|
}
|
|
|
|
Pass
|
|
{
|
|
Name "DepthOnly"
|
|
Tags { "LightMode" = "DepthOnly" }
|
|
|
|
ZWrite On
|
|
ColorMask R
|
|
Cull Back
|
|
|
|
HLSLPROGRAM
|
|
#pragma target 4.6
|
|
#pragma vertex TessVertex
|
|
#pragma hull TessHull
|
|
#pragma domain DepthDomain
|
|
#pragma fragment DepthFragment
|
|
|
|
struct DepthVaryings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
};
|
|
|
|
[domain("tri")]
|
|
DepthVaryings DepthDomain(TessFactors factors, OutputPatch<ControlPoint, 3> patch, float3 bary : SV_DomainLocation)
|
|
{
|
|
Attributes v = TessInterpolate(patch, bary);
|
|
DepthVaryings o;
|
|
o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
|
|
return o;
|
|
}
|
|
|
|
half DepthFragment(DepthVaryings input) : SV_Target
|
|
{
|
|
return input.positionCS.z;
|
|
}
|
|
ENDHLSL
|
|
}
|
|
|
|
Pass
|
|
{
|
|
// Deferred+'s normals prepass draws ONLY this tag (the forward path draws both), see the
|
|
// PlayerCamoLit fix - geometric (displaced) normal is enough for SSAO/HTrace.
|
|
Name "DepthNormalsOnly"
|
|
Tags { "LightMode" = "DepthNormalsOnly" }
|
|
|
|
ZWrite On
|
|
Cull Back
|
|
|
|
HLSLPROGRAM
|
|
#pragma target 4.6
|
|
#pragma vertex TessVertex
|
|
#pragma hull TessHull
|
|
#pragma domain DepthNormalsDomain
|
|
#pragma fragment DepthNormalsFragment
|
|
|
|
// Same as URP Lit's DepthNormals pass: decals need the rendering-layers target.
|
|
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
|
|
|
|
struct DepthNormalsVaryings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
float3 normalWS : TEXCOORD0;
|
|
};
|
|
|
|
[domain("tri")]
|
|
DepthNormalsVaryings DepthNormalsDomain(TessFactors factors, OutputPatch<ControlPoint, 3> patch, float3 bary : SV_DomainLocation)
|
|
{
|
|
Attributes v = TessInterpolate(patch, bary);
|
|
DepthNormalsVaryings o;
|
|
o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
|
|
o.normalWS = TransformObjectToWorldNormal(v.normalOS);
|
|
return o;
|
|
}
|
|
|
|
void DepthNormalsFragment(
|
|
DepthNormalsVaryings input
|
|
, out half4 outNormalWS : SV_Target0
|
|
#ifdef _WRITE_RENDERING_LAYERS
|
|
, out uint outRenderingLayers : SV_Target1
|
|
#endif
|
|
)
|
|
{
|
|
outNormalWS = half4(NormalizeNormalPerPixel(input.normalWS), 0);
|
|
#ifdef _WRITE_RENDERING_LAYERS
|
|
outRenderingLayers = EncodeMeshRenderingLayer();
|
|
#endif
|
|
}
|
|
ENDHLSL
|
|
}
|
|
|
|
Pass
|
|
{
|
|
// The project renders forward(+); the ONLY consumer of this pass is HTrace WSGI, which
|
|
// renders its thin GBuffer through the "UniversalGBuffer" tag and composites its traced
|
|
// irradiance from GBuffer0.rgb (albedo) / GBuffer1.a (occlusion) AFTER post-processing.
|
|
// Without it these pixels keep stale GBuffer data and get wrong injected GI (the cyan
|
|
// sky tint) - same failure class as the HeroCutout/PlayerCamo GBuffer fixes. Albedo is
|
|
// graded exactly like the forward pass (brightness/occlusion/contrast) so the injected
|
|
// indirect light matches the visible picture.
|
|
Name "GBuffer"
|
|
Tags { "LightMode" = "UniversalGBuffer" }
|
|
|
|
ZWrite On
|
|
ZTest LEqual
|
|
Cull Back
|
|
|
|
HLSLPROGRAM
|
|
#pragma target 4.6
|
|
#pragma exclude_renderers gles3 glcore
|
|
|
|
#pragma vertex TessVertex
|
|
#pragma hull TessHull
|
|
#pragma domain GBufferDomain
|
|
#pragma fragment GBufferFragment
|
|
|
|
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
|
|
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
|
|
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
|
|
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
|
|
#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
|
|
#pragma multi_compile_fragment _ _RENDER_PASS_ENABLED
|
|
#pragma multi_compile _ _CLUSTER_LIGHT_LOOP
|
|
#pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX
|
|
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
|
|
|
|
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
|
|
#pragma multi_compile _ SHADOWS_SHADOWMASK
|
|
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
|
|
#pragma multi_compile _ LIGHTMAP_ON
|
|
#pragma multi_compile_fragment _ LIGHTMAP_BICUBIC_SAMPLING
|
|
#pragma multi_compile_fragment _ REFLECTION_PROBE_ROTATION
|
|
#pragma multi_compile _ DYNAMICLIGHTMAP_ON
|
|
#pragma multi_compile _ USE_LEGACY_LIGHTMAPS
|
|
#pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT
|
|
#pragma multi_compile_fragment _ _SCREEN_SPACE_IRRADIANCE
|
|
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl"
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/GBufferOutput.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"
|
|
|
|
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
|
|
TEXTURE2D(_BumpMap); SAMPLER(sampler_BumpMap);
|
|
TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap);
|
|
|
|
struct GBufferVaryings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
float2 uv : TEXCOORD0;
|
|
float3 positionWS : TEXCOORD1;
|
|
float3 normalWS : TEXCOORD2;
|
|
float4 tangentWS : TEXCOORD3; // w = bitangent sign
|
|
DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 4);
|
|
#ifdef DYNAMICLIGHTMAP_ON
|
|
float2 dynamicLightmapUV : TEXCOORD5;
|
|
#endif
|
|
#ifdef USE_APV_PROBE_OCCLUSION
|
|
float4 probeOcclusion : TEXCOORD6;
|
|
#endif
|
|
};
|
|
|
|
[domain("tri")]
|
|
GBufferVaryings GBufferDomain(TessFactors factors, OutputPatch<ControlPoint, 3> patch, float3 bary : SV_DomainLocation)
|
|
{
|
|
Attributes v = TessInterpolate(patch, bary);
|
|
VertexPositionInputs pos = GetVertexPositionInputs(v.positionOS.xyz);
|
|
VertexNormalInputs nrm = GetVertexNormalInputs(v.normalOS, v.tangentOS);
|
|
|
|
GBufferVaryings o = (GBufferVaryings)0;
|
|
o.positionCS = pos.positionCS;
|
|
o.positionWS = pos.positionWS;
|
|
o.normalWS = nrm.normalWS;
|
|
o.tangentWS = float4(nrm.tangentWS, v.tangentOS.w * GetOddNegativeScale());
|
|
o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
|
|
|
|
OUTPUT_LIGHTMAP_UV(v.staticLightmapUV, unity_LightmapST, o.staticLightmapUV);
|
|
#ifdef DYNAMICLIGHTMAP_ON
|
|
o.dynamicLightmapUV = v.dynamicLightmapUV * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
|
|
#endif
|
|
OUTPUT_SH4(pos.positionWS, o.normalWS, GetWorldSpaceNormalizeViewDir(pos.positionWS), o.vertexSH, o.probeOcclusion);
|
|
return o;
|
|
}
|
|
|
|
GBufferFragOutput GBufferFragment(GBufferVaryings input)
|
|
{
|
|
// Surface build mirrors ForwardFragment 1:1 (graded albedo, contrast'd occlusion).
|
|
half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;
|
|
half heightBr = SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, input.uv).r;
|
|
albedo.rgb *= lerp(1.0h, _Brightness, heightBr);
|
|
half3 normalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_BumpMap, sampler_BumpMap, input.uv), _BumpScale);
|
|
half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, input.uv).g;
|
|
occ = saturate((occ - 0.5h) * (1.0h + _OcclusionContrast) + 0.5h);
|
|
half occlusion = saturate(LerpWhiteTo(occ, _OcclusionStrength));
|
|
|
|
float sgn = input.tangentWS.w;
|
|
float3 bitangentWS = sgn * cross(input.normalWS, input.tangentWS.xyz);
|
|
half3x3 tbn = half3x3(input.tangentWS.xyz, bitangentWS, input.normalWS);
|
|
|
|
InputData inputData = (InputData)0;
|
|
inputData.positionWS = input.positionWS;
|
|
inputData.positionCS = input.positionCS;
|
|
inputData.normalWS = NormalizeNormalPerPixel(TransformTangentToWorld(normalTS, tbn));
|
|
inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
|
|
inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
|
|
#if defined(_MAIN_LIGHT_SHADOWS_SCREEN)
|
|
inputData.shadowCoord = float4(inputData.normalizedScreenSpaceUV, 0, 1);
|
|
#else
|
|
inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS);
|
|
#endif
|
|
|
|
#if defined(_SCREEN_SPACE_IRRADIANCE)
|
|
inputData.bakedGI = SAMPLE_GI(_ScreenSpaceIrradiance, input.positionCS.xy);
|
|
#elif defined(DYNAMICLIGHTMAP_ON)
|
|
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.dynamicLightmapUV, input.vertexSH, inputData.normalWS);
|
|
inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
|
|
#elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2))
|
|
inputData.bakedGI = SAMPLE_GI(input.vertexSH,
|
|
GetAbsolutePositionWS(inputData.positionWS),
|
|
inputData.normalWS,
|
|
inputData.viewDirectionWS,
|
|
input.positionCS.xy,
|
|
input.probeOcclusion,
|
|
inputData.shadowMask);
|
|
#else
|
|
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.vertexSH, inputData.normalWS);
|
|
inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
|
|
#endif
|
|
|
|
SurfaceData surfaceData = (SurfaceData)0;
|
|
surfaceData.albedo = saturate(albedo.rgb); // GBuffer0 is UNORM; HTrace multiplies GI by it
|
|
surfaceData.alpha = 1;
|
|
surfaceData.metallic = _Metallic;
|
|
surfaceData.smoothness = _Smoothness;
|
|
surfaceData.normalTS = normalTS;
|
|
surfaceData.occlusion = occlusion;
|
|
|
|
#if defined(_DBUFFER)
|
|
ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData);
|
|
#endif
|
|
|
|
BRDFData brdfData;
|
|
InitializeBRDFData(surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.alpha, brdfData);
|
|
|
|
Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask);
|
|
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, inputData.shadowMask);
|
|
|
|
half3 color = GlobalIllumination(brdfData, (BRDFData)0, 0,
|
|
inputData.bakedGI, surfaceData.occlusion, inputData.positionWS,
|
|
inputData.normalWS, inputData.viewDirectionWS, inputData.normalizedScreenSpaceUV);
|
|
|
|
return PackGBuffersBRDFData(brdfData, inputData, surfaceData.smoothness, color, surfaceData.occlusion);
|
|
}
|
|
ENDHLSL
|
|
}
|
|
}
|
|
|
|
FallBack "Universal Render Pipeline/Lit"
|
|
}
|