#ifndef ALLIN13DSHADER_HELPER_URP_INCLUDED
#define ALLIN13DSHADER_HELPER_URP_INCLUDED

#define NUM_ADDITIONAL_LIGHTS GetNumAdditionalLights();

#define OBJECT_TO_WORLD_MATRIX GetObjectToWorldMatrix()

#if defined(ALLIN1_DECALS_SUPPORT) && defined(_DBUFFER)
	#define ALLIN1_DECALS_READY_TO_USE
	
	#if defined(_DBUFFER_MRT1) || defined(_DBUFFER_MRT2) || defined(_DBUFFER_MRT3)
		#define ALLIN1_DECAL_MODE_DBUFFER
	#else
		#define ALLIN1_DECAL_MODE_SCREEN_SPACE
	#endif
#endif

#ifdef REQUIRE_SCENE_DEPTH

	#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
	
	float GetRawDepth(float4 projPos)
	{
		float res = SampleSceneDepth(projPos.xy / projPos.w);
		return res;
	}

	float GetLinearDepth01(float4 projPos)
	{
		float rawDepth = GetRawDepth(projPos);
		float res = Linear01Depth (rawDepth, _ZBufferParams);

		return res;
	}
	
	float GetSceneDepthDiff(float4 projPos)
	{
		float rawDepth = GetRawDepth(projPos);
		float res = LinearEyeDepth(rawDepth, _ZBufferParams) - projPos.z;

		return res;
	}

#endif

float3 GetNormalWS(float3 normalOS)
{
	float3 normalWS = TransformObjectToWorldNormal(normalOS);
	return normalWS;
}

float3 GetViewDirWS(float3 vertexWS)
{
	float3 res = GetWorldSpaceViewDir(vertexWS);
	return res;
}

float3 GetPositionVS(float3 positionOS)
{
	float3 res = TransformWorldToView(positionOS);
	return res;
}

float3 GetPositionWS(float4 positionOS)
{
	return TransformObjectToWorld(positionOS.xyz);
}

float3 GetPositionOS(float4 positionWS)
{
	return TransformWorldToObject(positionWS.xyz);

}

float3 GetDirWS(float4 dirOS)
{
	return TransformObjectToWorldDir(dirOS.xyz);
}

float3 GetDirOSFloat3(float3 dirOS)
{
	return TransformObjectToWorldDir(dirOS.xyz);
}

float3 GetDirOS(float4 dirOS)
{
	return GetDirOSFloat3(dirOS.xyz);
}

float3 GetMainLightDir(float3 vertexWS)
{
#ifdef _LIGHTMODEL_FASTLIGHTING
	float3 res = global_lightDirection;
#else
	float3 res = GetMainLight().direction;
#endif
	
	return res;
}

float3 GetMainLightColorRGB()
{
#ifdef _LIGHTMODEL_FASTLIGHTING
	float3 res = global_lightColor.rgb;
#else
	float3 res = GetMainLight().color;
#endif

	return res;
}

float2 GetSSAO(float2 normalizedScreenSpaceUV, float alpha)
{
	AmbientOcclusionFactor aoFactorURP = GetScreenSpaceAmbientOcclusion(normalizedScreenSpaceUV);
		
#if defined(_ALLIN13D_SURFACE_TRANSPARENT)
	float2 res = float2(1, 1);
#else
	float2 res = float2(aoFactorURP.directAmbientOcclusion, aoFactorURP.indirectAmbientOcclusion);
#endif
	
	return res;
}

//normalWS needed for the equivalent method in BIRP
//effectsData needed for the equivalent method in BIRP
AllIn1LightData GetPointLightData(int index, float3 vertexWS, float3 normalWS, EffectsData effectsData, AllIn1GI gi)
{
	AllIn1LightData lightData;

	Light additionalLight = GetAdditionalLight(index, vertexWS, gi.shadowMask);
	lightData.lightColor = additionalLight.color;
	lightData.lightDir = additionalLight.direction;

#ifdef _CLUSTER_LIGHT_LOOP 
	int lightIndex = index;
#else
	int lightIndex = GetPerObjectLightIndex(index);
#endif

#if defined(_LIGHT_COOKIES)
    float3 cookieColor = SampleAdditionalLightCookie(lightIndex, effectsData.vertexWS);
    lightData.lightColor *= cookieColor;
#endif

#ifdef _RECEIVE_SHADOWS_ON
	lightData.realtimeShadow = additionalLight.shadowAttenuation;
#else
	lightData.realtimeShadow = 1.0;
#endif
	
	lightData.distanceAttenuation = additionalLight.distanceAttenuation;
	lightData.shadowColor = lightData.realtimeShadow;
	lightData.layerMask = additionalLight.layerMask;
	
	return lightData;
}

AllIn1LightData GetMainLightData(float3 vertexWS, EffectsData effectsData, AllIn1GI gi)
{
	AllIn1LightData lightData;
	
#if defined(_LIGHTMODEL_NONE)
	lightData.lightColor = float3(1.0, 1.0, 1.0);
	lightData.lightDir = float3(0.0, 1.0, 0.0);
	lightData.distanceAttenuation = 1.0;
	lightData.shadowColor = 1.0;
	lightData.realtimeShadow = 1.0;
	lightData.layerMask = 1;
#elif defined(_LIGHTMODEL_FASTLIGHTING)
	lightData.lightColor = global_lightColor;
	lightData.lightDir = global_lightDirection;
	lightData.distanceAttenuation = 1.0;
	lightData.shadowColor = 1.0;
	lightData.realtimeShadow = 1.0;
	lightData.layerMask = 1;
#else
	Light mainLight = GetMainLight(); 
	lightData.lightColor = mainLight.color;
	lightData.lightDir = mainLight.direction;

	lightData.distanceAttenuation = mainLight.distanceAttenuation;
	
	float4 shadowCoords = TransformWorldToShadowCoord(vertexWS);
	#ifdef _RECEIVE_SHADOWS_ON
		lightData.realtimeShadow = MainLightRealtimeShadow(shadowCoords);
		#if defined(_RECEIVEDSHADOWSTYPE_STYLIZED)
			lightData.realtimeShadow = AntiAliasing(lightData.realtimeShadow, ACCESS_PROP_FLOAT(_ShadowCutoff));
		#endif

		float shadowFade = GetMainLightShadowFade(effectsData.vertexWS);
		float shadowMask = 1.0;
		#if defined(SHADOWS_SHADOWMASK)
			shadowMask = gi.shadowMask.r;
		#endif
		lightData.realtimeShadow = lerp(lightData.realtimeShadow, shadowMask, shadowFade);
	#else
		lightData.realtimeShadow = 1.0;
	#endif

	lightData.shadowColor = lightData.realtimeShadow;
	lightData.layerMask = mainLight.layerMask;

	#if defined(_LIGHT_COOKIES)
        float3 cookieColor = SampleMainLightCookie(effectsData.vertexWS);
        lightData.lightColor *= cookieColor;
    #endif
#endif
	
	return lightData;
}

int GetNumAdditionalLights()
{
	return GetAdditionalLightsCount();
}

/*Needed for URP shadow caster pass*/
float3 _LightDirection;
float3 _LightPosition;
/************/
FragmentDataShadowCaster GetClipPosShadowCaster(VertexData v, FragmentDataShadowCaster input)
{
    float3 positionWS = TransformObjectToWorld(v.vertex.xyz);
    float3 normalWS = TransformObjectToWorldNormal(v.normal);

#if _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

	input.pos = positionCS;
    return input;
}

ShadowCoordStruct GetShadowCoords(VertexData v, float4 clipPos, float3 vertexWS, float2 uvLightmap)
{
	ShadowCoordStruct res;

	res._ShadowCoord = 0;
	res.pos = clipPos;
	
	return res;
}

float GetShadowAttenuation(EffectsData data)
{
	float res = 1.0;

#if !defined(_LIGHTMODEL_FASTLIGHTING)

	float4 shadowCoords = TransformWorldToShadowCoord(data.vertexWS);

	float mainLightShadow = MainLightRealtimeShadow(shadowCoords);
	
	//int numAdditionalLights = NUM_ADDITIONAL_LIGHTS;
	//float additionalLightsShadows = 1.0;
	res = mainLightShadow;
	//for(int lightIndex = 0; lightIndex < numAdditionalLights; lightIndex++)
	//{
	//	Light additionalLight = GetAdditionalLight(lightIndex, vertexWS);
	//	float additionalLightShadow = AdditionalLightRealtimeShadow(lightIndex, vertexWS/*, additionalLight.direction*/) * additionalLight.distanceAttenuation;
		
	//	res *= additionalLightShadow;
	//}	
#endif

	return res;
}

//float GetShadowAttenuation(float3 vertexWS)
//{
//	float4 shadowCoords = TransformWorldToShadowCoord(vertexWS);
//	float mainLightShadow = MainLightRealtimeShadow(shadowCoords);

//	float res = mainLightShadow;
//	int numAdditionalLights = NUM_ADDITIONAL_LIGHTS;
//	for(int lightIndex = 0; lightIndex < numAdditionalLights; lightIndex++)
//	{
//		Light additionalLight = GetAdditionalLight(lightIndex, vertexWS);
//		float additionalLightShadow = AdditionalLightRealtimeShadow(lightIndex, vertexWS) * additionalLight.distanceAttenuation;
		
//		res += additionalLightShadow;
//	}
	
//	res = 1.0;
//	return res;
//}

float3 GetLightmap(float2 uvLightmap, EffectsData data)
{
	float3 res = 0.0;

#if defined(_AFFECTED_BY_LIGHTMAPS_ON) && defined(LIGHTMAP_ON)
	res = SampleLightmap(uvLightmap, data.normalWS);
	#ifdef SUBTRACTIVE_LIGHTING
		AllIn1LightData mainLight = GetMainLightData(data.vertexWS, data);
		float attenuation = mainLight.realtimeShadow;
		float ndotl = saturate(dot(data.normalWS, mainLight.lightDir));
		float3 shadowedLightEstimate =
				ndotl * (1 - attenuation) * mainLight.lightColor.rgb;
		float3 subtractedLight = res - shadowedLightEstimate;
		subtractedLight = max(subtractedLight, _SubtractiveShadowColor.xyz);
		subtractedLight = lerp(subtractedLight, res, attenuation);
	
		res = subtractedLight;
	#endif
#endif

	return res;
}

uint AllIn1GetMeshRenderingLayer()
{
	uint res;
	#if UNITY_VERSION >= 202230
		res = GetMeshRenderingLayer();
	#else
		res = GetMeshRenderingLightLayer();
	#endif
	
	return res;
}

float3 GetAmbientColor(EffectsData data)
{
	float3 res = float3(0, 0, 0);
#if !defined(LIGHTMAP_ON)
	#if defined(_CUSTOM_AMBIENT_LIGHT_ON)
		res = ACCESS_PROP_FLOAT4(_CustomAmbientColor).rgb;
	#else
		#if defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)
			half3 bakedGI;
			float4 probeOcclusion;
			EvaluateAdaptiveProbeVolume(data.vertexWS.xyz, data.normalWS.xyz, data.viewDirWS.xyz,
									  data.projPos.xy, AllIn1GetMeshRenderingLayer(), 
									  bakedGI, probeOcclusion);
			res = bakedGI;
		#else
			res = SampleSH(data.normalWS);
		#endif
	#endif

#endif

	return res;
}

AllIn1GI CalculateGI(float2 uvLightmap, EffectsData data)
{
	AllIn1GI res;
	INIT_GI(res);

#if defined(LIGHTMAP_ON)
	res.diffuse = GetLightmap(uvLightmap, data);
#else
	res.diffuse = GetAmbientColor(data);
#endif

#if defined(SHADOWS_SHADOWMASK)
	res.shadowMask = SAMPLE_TEXTURE2D(unity_ShadowMask, samplerunity_ShadowMask, uvLightmap);
#endif

	return res;
}

float GetFogFactor(float4 clipPos)
{
	float res = 0;

#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
	res = ComputeFogFactor(clipPos.z);
#endif

	return res;
}

#if defined(FOG_ENABLED)
float4 CustomMixFog(float fogFactor, float4 col)
{
	float4 res = col;
	res.rgb = MixFog(res.rgb, fogFactor);
	return res;
}
#endif

void ConfigureDecalData(inout AllIn1DecalData allIn1Decal, float4 positionCS)
{
#if defined(_DBUFFER)
	FETCH_DBUFFER(DBuffer, _DBufferTexture, int2(positionCS.xy));

    DecalSurfaceData decalSurfaceData;
    DECODE_FROM_DBUFFER(DBuffer, decalSurfaceData);

	allIn1Decal.baseColor = decalSurfaceData.baseColor;
	allIn1Decal.emissive = decalSurfaceData.emissive;
	allIn1Decal.mask = step(0.1, allIn1Decal.baseColor.a);
	allIn1Decal.mask = saturate(allIn1Decal.mask + decalSurfaceData.normalWS.w);
	allIn1Decal.MAOSAlpha = decalSurfaceData.MAOSAlpha;
	allIn1Decal.smoothness = decalSurfaceData.smoothness;
	allIn1Decal.metallic = decalSurfaceData.metallic;

	float4 normalTS = decalSurfaceData.normalWS; //Even if it's called normalWS, the content is a sampled normal map in tangent space

	allIn1Decal.normalTS = lerp(normalTS, DEFAULT_NORMAL_MAP_VALUE, allIn1Decal.mask);
	allIn1Decal.unpackedNormal = UnpackNormal(allIn1Decal.normalTS);
#endif
}

#ifdef REFLECTIONS_ON

float3 BoxProjection (
	float3 direction, float3 position,
	float4 cubemapPosition, float3 boxMin, float3 boxMax
) {
	#if UNITY_SPECCUBE_BOX_PROJECTION
		UNITY_BRANCH
		if (cubemapPosition.w > 0) {
			float3 factors =
				((direction > 0 ? boxMax : boxMin) - position) / direction;
			float scalar = min(min(factors.x, factors.y), factors.z);
			direction = direction * scalar + (position - cubemapPosition.xyz);
		}
	#endif
	return direction;
}

float3 GetReflectionsSimple(float3 worldRefl, float cubeLod, float3 positionWS)
{
	half probe0Volume = CalculateProbeVolumeSqrMagnitude(unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
    half probe1Volume = CalculateProbeVolumeSqrMagnitude(unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);

    half volumeDiff = probe0Volume - probe1Volume;
    float importanceSign = unity_SpecCube1_BoxMin.w;

	float desiredWeightProbe0 = CalculateProbeWeight(positionWS, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
    float desiredWeightProbe1 = CalculateProbeWeight(positionWS, unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);

	// A probe is dominant if its importance is higher
    // Or have equal importance but smaller volume
    bool probe0Dominant = importanceSign > 0.0f || (importanceSign == 0.0f && volumeDiff < -0.0001h);
    bool probe1Dominant = importanceSign < 0.0f || (importanceSign == 0.0f && volumeDiff > 0.0001h);

    float weightProbe0 = probe1Dominant ? min(desiredWeightProbe0, 1.0f - desiredWeightProbe1) : desiredWeightProbe0;
    float weightProbe1 = probe0Dominant ? min(desiredWeightProbe1, 1.0f - desiredWeightProbe0) : desiredWeightProbe1;

    float totalWeight = weightProbe0 + weightProbe1;

    // If either probe 0 or probe 1 is dominant the sum of weights is guaranteed to be 1.
    // If neither is dominant this is not guaranteed - only normalize weights if totalweight exceeds 1.
    weightProbe0 /= max(totalWeight, 1.0f);
    weightProbe1 /= max(totalWeight, 1.0f);

	float3 res = 0;
	if(weightProbe0 > 0.01)
	{
		float3 reflUV0 = BoxProjection(worldRefl, positionWS, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin.xyz, unity_SpecCube0_BoxMax.xyz);
		float4 probe0HDR = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, reflUV0, cubeLod);
		float3 probe0 = DecodeHDREnvironment(probe0HDR, unity_SpecCube0_HDR);

		res += weightProbe0 * probe0;
	}
	if(weightProbe1 > 0.01f) 
	{
		float3 reflUV1 = BoxProjection(worldRefl, positionWS, unity_SpecCube1_ProbePosition, unity_SpecCube1_BoxMin.xyz, unity_SpecCube1_BoxMax.xyz);
		float4 probe1HDR = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube1, samplerunity_SpecCube0, reflUV1, cubeLod);
		float3 probe1 = DecodeHDREnvironment(probe1HDR, unity_SpecCube1_HDR);

		res += weightProbe1 * probe1;
	}
	if(totalWeight < 0.99)
	{
		float4 glossyEnvironmentHDR = SAMPLE_TEXTURECUBE_LOD(_GlossyEnvironmentCubeMap, sampler_GlossyEnvironmentCubeMap, worldRefl, cubeLod);
		float3 glossyEnvironment = DecodeHDREnvironment(glossyEnvironmentHDR, _GlossyEnvironmentCubeMap_HDR);

		res += (1.0f - totalWeight) * glossyEnvironment;
	}

	return res;
}

float3 GetReflectionsCluster(float3 positionWS, float2 normalizedScreenSpaceUV, float3 worldRefl, float cubeLod)
{
	float3 res = 0;
	float3 irradiance = float3(0, 0, 0);
	float mip = cubeLod;
#if USE_CLUSTER_LIGHT_LOOP && CLUSTER_HAS_REFLECTION_PROBES
	float totalWeight = 0.0f;
	uint probeIndex;
	ClusterIterator it = ClusterInit(normalizedScreenSpaceUV, positionWS, 1);
	[loop] while (ClusterNext(it, probeIndex) && totalWeight < 0.99f)
	{
		probeIndex -= URP_FP_PROBES_BEGIN;

		float weight = CalculateProbeWeight(positionWS, urp_ReflProbes_BoxMin[probeIndex], urp_ReflProbes_BoxMax[probeIndex]);
		weight = min(weight, 1.0f - totalWeight);

		half3 sampleVector = worldRefl;
	#ifdef _REFLECTION_PROBE_BOX_PROJECTION
			sampleVector = BoxProjectedCubemapDirection(worldRefl, positionWS, urp_ReflProbes_ProbePosition[probeIndex], urp_ReflProbes_BoxMin[probeIndex], urp_ReflProbes_BoxMax[probeIndex]);
	#endif // _REFLECTION_PROBE_BOX_PROJECTION

		uint maxMip = (uint)abs(urp_ReflProbes_ProbePosition[probeIndex].w) - 1;
		half probeMip = min(mip, maxMip);
		float2 uv = saturate(PackNormalOctQuadEncode(sampleVector) * 0.5 + 0.5);

		float mip0 = floor(probeMip);
		float mip1 = mip0 + 1;
		float mipBlend = probeMip - mip0;
		float4 scaleOffset0 = urp_ReflProbes_MipScaleOffset[probeIndex * 7 + (uint)mip0];
		float4 scaleOffset1 = urp_ReflProbes_MipScaleOffset[probeIndex * 7 + (uint)mip1];

		half3 irradiance0 = half4(SAMPLE_TEXTURE2D_LOD(urp_ReflProbes_Atlas, sampler_LinearClamp, uv * scaleOffset0.xy + scaleOffset0.zw, 0.0)).rgb;
		half3 irradiance1 = half4(SAMPLE_TEXTURE2D_LOD(urp_ReflProbes_Atlas, sampler_LinearClamp, uv * scaleOffset1.xy + scaleOffset1.zw, 0.0)).rgb;
		res += weight * lerp(irradiance0, irradiance1, mipBlend);
		totalWeight += weight;
	}
#else
	half probe0Volume = CalculateProbeVolumeSqrMagnitude(unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
    half probe1Volume = CalculateProbeVolumeSqrMagnitude(unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);

    half volumeDiff = probe0Volume - probe1Volume;
    float importanceSign = unity_SpecCube1_BoxMin.w;

    // A probe is dominant if its importance is higher
    // Or have equal importance but smaller volume
    bool probe0Dominant = importanceSign > 0.0f || (importanceSign == 0.0f && volumeDiff < -0.0001h);
    bool probe1Dominant = importanceSign < 0.0f || (importanceSign == 0.0f && volumeDiff > 0.0001h);

    float desiredWeightProbe0 = CalculateProbeWeight(positionWS, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
    float desiredWeightProbe1 = CalculateProbeWeight(positionWS, unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);

    // Subject the probes weight if the other probe is dominant
    float weightProbe0 = probe1Dominant ? min(desiredWeightProbe0, 1.0f - desiredWeightProbe1) : desiredWeightProbe0;
    float weightProbe1 = probe0Dominant ? min(desiredWeightProbe1, 1.0f - desiredWeightProbe0) : desiredWeightProbe1;

    float totalWeight = weightProbe0 + weightProbe1;

    // If either probe 0 or probe 1 is dominant the sum of weights is guaranteed to be 1.
    // If neither is dominant this is not guaranteed - only normalize weights if totalweight exceeds 1.
    weightProbe0 /= max(totalWeight, 1.0f);
    weightProbe1 /= max(totalWeight, 1.0f);

    // Sample the first reflection probe
    if (weightProbe0 > 0.01f)
    {
        half3 reflectVector0 = worldRefl;
#ifdef _REFLECTION_PROBE_BOX_PROJECTION
        reflectVector0 = BoxProjectedCubemapDirection(worldRefl, positionWS, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
#endif // _REFLECTION_PROBE_BOX_PROJECTION

        half4 encodedIrradiance = half4(SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, reflectVector0, mip));

        irradiance += weightProbe0 * DecodeHDREnvironment(encodedIrradiance, unity_SpecCube0_HDR);
    }

    // Sample the second reflection probe
    if (weightProbe1 > 0.01f)
    {
        half3 reflectVector1 = worldRefl;
#ifdef _REFLECTION_PROBE_BOX_PROJECTION
        worldRefl = BoxProjectedCubemapDirection(worldRefl, positionWS, unity_SpecCube1_ProbePosition, unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);
#endif // _REFLECTION_PROBE_BOX_PROJECTION
        half4 encodedIrradiance = half4(SAMPLE_TEXTURECUBE_LOD(unity_SpecCube1, samplerunity_SpecCube1, worldRefl, mip));

        irradiance += weightProbe1 * DecodeHDREnvironment(encodedIrradiance, unity_SpecCube1_HDR);
    }

	res = irradiance;
#endif

    // Use any remaining weight to blend to environment reflection cube map
    if (totalWeight < 0.99f)
    {
        half4 encodedIrradiance = half4(SAMPLE_TEXTURECUBE_LOD(_GlossyEnvironmentCubeMap, sampler_GlossyEnvironmentCubeMap, worldRefl, mip));

        res += (1.0f - totalWeight) * DecodeHDREnvironment(encodedIrradiance, _GlossyEnvironmentCubeMap_HDR);
    }
	
	return res;
}

float3 GetSkyColor(float3 positionWS, float2 normalizedScreenSpaceUV, float3 normalWS, float3 viewDirWS, float cubeLod)
{
	float3 worldRefl = normalize(reflect(-viewDirWS, normalWS));
	float4 skyData = 0;
	float3 res = 0;

#ifdef _REFLECTION_PROBE_BLENDING
	#if USE_CLUSTER_LIGHT_LOOP && CLUSTER_HAS_REFLECTION_PROBES
		res = GetReflectionsCluster(positionWS, normalizedScreenSpaceUV, worldRefl, cubeLod);
	#else
		res = GetReflectionsSimple(worldRefl, cubeLod, positionWS);
	#endif
#else
	res = GetReflectionsSimple(worldRefl, cubeLod, positionWS);
#endif

#ifdef _REFLECTIONS_TOON
	float posterizationLevel = lerp(2, 20, ACCESS_PROP_FLOAT(_ToonFactor));
	res = floor(res * posterizationLevel) / posterizationLevel;
#endif

	res *= ACCESS_PROP_FLOAT(_ReflectionsAtten);

	return res;
}
#endif

float3 ShadeSH(float4 normalWS)
{
	float3 res = SampleSH(normalWS.xyz);
	return res;
}

#define OBJECT_TO_CLIP_SPACE(v) TransformObjectToHClip(v.vertex.xyz)
#define OBJECT_TO_CLIP_SPACE_FLOAT4(pos) TransformObjectToHClip(pos.xyz)
#define ALLIN1_APPLY_CROSSFADE(input) LODFadeCrossFade(input.pos);

#endif