Files
chameleon/Assets/Shaders/PlayerCamoGBuffer.hlsl
adminandClaude Fable 5 6db78397da Apply camo brightness/saturation grade in GBuffer pass for HTrace GI
HTrace WSGI composites indirect lighting after post-processing using the
GBuffer albedo/occlusion of the player's stock URP GBuffer pass. That pass
knew neither the painted-height AO semantics nor the brightness/saturation
grade, so desaturated paint reappeared fully saturated in shadow/indirect
light and painted height modulated GI with the raw 0.5-centered value.

- PlayerCamoShared.hlsl (new): single source for camo knobs,
  ApplyPaintedHeightSurface and ApplyCamoBodyGrade shared by both passes
- PlayerCamoGBuffer.hlsl (new): CamoGBufferFragment = URP 17.3 Lit GBuffer
  fragment + height-AO remap on occlusion + grade applied to albedo
- PlayerCamoLit.shader: GBuffer pass now uses CamoGBufferFragment
- PlayerCamo.hlsl: consumes the shared helpers (forward path unchanged)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 13:24:52 +02:00

72 lines
3.3 KiB
HLSL

#ifndef PLAYER_CAMO_GBUFFER_INCLUDED
#define PLAYER_CAMO_GBUFFER_INCLUDED
// Camo variant of URP's LitGBufferPassFragment. Include AFTER LitGBufferPass.hlsl (reuses its
// Varyings / InitializeInputData / InitializeBakedGIData).
//
// The project renders FORWARD, so this GBuffer pass has exactly ONE consumer: HTrace WSGI renders
// its thin GBuffer through the "UniversalGBuffer" tag and its GI composite multiplies the traced
// irradiance by GBuffer0.rgb (diffuse albedo) and GBuffer1.a (occlusion), ADDING the result after
// post-processing - i.e. after the forward pass's camo grade already ran. Two camo hooks keep that
// injected indirect light consistent with the graded forward look:
// 1. ApplyPaintedHeightSurface: forward AO semantics for the painted height (neutral 0.5 -> AO 1)
// instead of the raw 0.5-centred height value in GBuffer1.a, + the relief normal.
// 2. ApplyCamoBodyGrade on the albedo: brightness/saturation follow into the injected GI, so
// desaturated paint stays desaturated in shadows / under indirect light.
// Everything else is a faithful copy of URP 17.3 LitGBufferPass.hlsl's fragment.
#include "PlayerCamoShared.hlsl"
GBufferFragOutput CamoGBufferFragment(Varyings input)
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
#if defined(_PARALLAXMAP)
#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
half3 viewDirTS = input.viewDirTS;
#else
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
half3 viewDirTS = GetViewDirectionTangentSpace(input.tangentWS, input.normalWS, viewDirWS);
#endif
ApplyPerPixelDisplacement(viewDirTS, input.uv);
#endif
SurfaceData surfaceData;
InitializeStandardLitSurfaceData(input.uv, surfaceData);
// Camo hooks (see header). The albedo grade is clamped: GBuffer0 is UNORM, and HTrace multiplies
// its GI by the stored value, so out-of-range brightness would clip there anyway.
ApplyPaintedHeightSurface(input.uv, surfaceData);
ApplyCamoBodyGrade(surfaceData.albedo);
surfaceData.albedo = saturate(surfaceData.albedo);
#ifdef LOD_FADE_CROSSFADE
LODFadeCrossFade(input.positionCS);
#endif
InputData inputData;
InitializeInputData(input, surfaceData.normalTS, inputData);
SETUP_DEBUG_TEXTURE_DATA(inputData, UNDO_TRANSFORM_TEX(input.uv, _BaseMap));
#if defined(_DBUFFER)
ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData);
#endif
InitializeBakedGIData(input, inputData);
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, surfaceData.emission + color, surfaceData.occlusion);
}
#endif