You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

469 lines
18 KiB

#ifndef UNITY_LIGHT_LOOP_DEF_INCLUDED
#define UNITY_LIGHT_LOOP_DEF_INCLUDED
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/CookieSampling.hlsl"
#define DWORD_PER_TILE 16 // See dwordsPerTile in LightLoop.cs, we have roomm for 31 lights and a number of light value all store on 16 bit (ushort)
// Depending if we are in ray tracing or not we need to use a different set of environement data
#if defined(WORLD_ENVIRONMENT_DATA) || defined(SHADER_STAGE_RAY_TRACING)
#define PLANAR_CAPTURE_VP _PlanarCaptureVPWL
#define PLANAR_SCALE_OFFSET _PlanarScaleOffsetWL
#define CUBE_SCALE_OFFSET _CubeScaleOffsetWL
#else
#define PLANAR_CAPTURE_VP _PlanarCaptureVP
#define PLANAR_SCALE_OFFSET _PlanarScaleOffset
#define CUBE_SCALE_OFFSET _CubeScaleOffset
#endif
// Some file may not required HD shadow context at all. In this case provide an empty one
// Note: if a double defintion error occur it is likely have include HDShadow.hlsl (and so HDShadowContext.hlsl) after lightloopdef.hlsl
#ifndef HAVE_HD_SHADOW_CONTEXT
struct HDShadowContext { float unused; };
#endif
// LightLoopContext is not visible from Material (user should not use these properties in Material file)
// It allow the lightloop to have transmit sampling information (do we use atlas, or texture array etc...)
struct LightLoopContext
{
int sampleReflection;
#ifdef APPLY_FOG_ON_SKY_REFLECTIONS
float3 positionWS; // For sky reflection, we need position to evalute height base fog
#endif
HDShadowContext shadowContext;
uint contactShadow; // a bit mask of 24 bits that tell if the pixel is in a contact shadow or not
real contactShadowFade; // combined fade factor of all contact shadows
SHADOW_TYPE shadowValue; // Stores the value of the cascade shadow map
};
// LightLoopOutput is the output of the LightLoop fuction call.
// It allow to retrieve the data output by the LightLoop
struct LightLoopOutput
{
float3 diffuseLighting;
float3 specularLighting;
};
//-----------------------------------------------------------------------------
// Reflection probe / Sky sampling function
// ----------------------------------------------------------------------------
#define SINGLE_PASS_CONTEXT_SAMPLE_REFLECTION_PROBES 0
#define SINGLE_PASS_CONTEXT_SAMPLE_SKY 1
#define REFL_ATLAS_CUBE_PADDING _ReflectionAtlasCubeData.xy
#define REFL_ATLAS_CUBE_MIP_PADDING _ReflectionAtlasCubeData.z
#define REFL_ATLAS_PLANAR_PADDING _ReflectionAtlasPlanarData.xy
#define REFL_ATLAS_TEXEL_SIZE _ReflectionAtlasPlanarData.zw
float2 GetReflectionAtlasCoordsCube(float4 scaleOffset, float3 dir, float lod)
{
float2 uv = saturate(PackNormalOctQuadEncode(dir) * 0.5 + 0.5);
float2 padding = REFL_ATLAS_CUBE_PADDING;
// Every octahedral mip has at least one texel padding. This improves octahedral mapping as it always should have border for every mip.
padding *= pow(2.0, max(lod - REFL_ATLAS_CUBE_MIP_PADDING, 0.0));
float2 size = scaleOffset.xy - padding;
float2 offset = scaleOffset.zw + 0.5 * padding;
return uv * size + offset;
}
float2 GetReflectionAtlasCoordsPlanar(float4 scaleOffset, float2 uv, float lod)
{
float2 padding = REFL_ATLAS_PLANAR_PADDING;
float2 size = scaleOffset.xy - padding;
float2 offset = scaleOffset.zw + 0.5 * padding;
float2 paddingClamp = REFL_ATLAS_TEXEL_SIZE * pow(2.0, ceil(lod)) * 0.5;
float2 minClamp = scaleOffset.zw + paddingClamp;
float2 maxClamp = scaleOffset.zw + scaleOffset.xy - paddingClamp;
return clamp(uv * size + offset, minClamp, maxClamp);
}
// The EnvLightData of the sky light contains a bunch of compile-time constants.
// This function sets them directly to allow the compiler to propagate them and optimize the code.
EnvLightData InitSkyEnvLightData(int envIndex)
{
EnvLightData output;
ZERO_INITIALIZE(EnvLightData, output);
output.lightLayers = 0xFFFFFFFF; // Enable sky for all layers
output.influenceShapeType = ENVSHAPETYPE_SKY;
// 31 bit index, 1 bit cache type
output.envIndex = envIndex;
output.influenceForward = float3(0.0, 0.0, 1.0);
output.influenceUp = float3(0.0, 1.0, 0.0);
output.influenceRight = float3(1.0, 0.0, 0.0);
output.influencePositionRWS = float3(0.0, 0.0, 0.0);
output.weight = 1.0;
output.multiplier = _EnableSkyReflection.x != 0 ? 1.0 : 0.0;
output.roughReflections = 1.0;
output.distanceBasedRoughness = 0.0;
// proxy
output.proxyForward = float3(0.0, 0.0, 1.0);
output.proxyUp = float3(0.0, 1.0, 0.0);
output.proxyRight = float3(1.0, 0.0, 0.0);
output.minProjectionDistance = 65504.0f;
return output;
}
// Variant environment data that provides a better default behavior for screen space refraction, when no proxy volume is available.
EnvLightData InitDefaultRefractionEnvLightData(int envIndex)
{
EnvLightData output = InitSkyEnvLightData(envIndex);
// For screen space refraction, instead of an infinite projection, utilize the renderer's extents.
output.proxyExtents = GetRendererExtents();
// Revert the infinite projection.
output.minProjectionDistance = 0;
return output;
}
bool IsEnvIndexCubemap(int index) { return index >= 0; }
bool IsEnvIndexTexture2D(int index) { return index < 0; }
float EdgeSampleFade(float2 positionNDC, float2 samplePositionNDC, float amplitude = 100.0f)
{
float2 ndc = samplePositionNDC;
float2 rcoords = abs(saturate(ndc.xy) * 2.0 - 1.0);
// When the object normal is not aligned with the reflection plane, the reflected ray might deviate too much and go out
// of the reflection frustum. So we apply blending when the reflection sample coords are on the edges of the texture
// These "edges" depend on the screen space coordinates of the pixel, because it is expected that a pixel on the
// edge of the screen will sample on the edge of the texture
// Blending factors taking the above into account
bool2 blend = (positionNDC < ndc.xy) ^ (ndc.xy < 0.5);
float2 alphas = saturate(amplitude * abs(ndc.xy - positionNDC));
alphas = float2(Smoothstep01(alphas.x), Smoothstep01(alphas.y));
float2 weights = lerp(1.0, saturate(2.0 - 2.0 * rcoords), blend * alphas);
return weights.x * weights.y;
}
// Note: index is whatever the lighting architecture want, it can contain information like in which texture to sample (in case we have a compressed BC6H texture and an uncompressed for real time reflection ?)
// EnvIndex can also be use to fetch in another array of struct (to atlas information etc...).
// Cubemap : texCoord = direction vector
// Texture2D : texCoord = projectedPositionWS - lightData.capturePosition
float4 SampleEnv(LightLoopContext lightLoopContext, int index, float3 texCoord, float lod, float rangeCompressionFactorCompensation, float2 positionNDC, int sliceIdx = 0)
{
// 31 bit index, 1 bit cache type
uint cacheType = IsEnvIndexCubemap(index) ? ENVCACHETYPE_CUBEMAP : ENVCACHETYPE_TEXTURE2D;
// Index start at 1, because -0 == 0, so we can't known which cache to sample for that index. Thus it is invalid.
index = abs(index) - 1;
float4 color = float4(0.0, 0.0, 0.0, 1.0);
// This code will be inlined as lightLoopContext is hardcoded in the light loop
if (lightLoopContext.sampleReflection == SINGLE_PASS_CONTEXT_SAMPLE_REFLECTION_PROBES)
{
if (cacheType == ENVCACHETYPE_TEXTURE2D)
{
//_ReflAtlasPlanarCaptureVP is in capture space
float4 samplePosCS = ComputeClipSpacePosition(texCoord, PLANAR_CAPTURE_VP[index]);
#if UNITY_UV_STARTS_AT_TOP
samplePosCS.y = -samplePosCS.y;
#endif
float2 ndc = samplePosCS.xy * rcp(samplePosCS.w);
ndc.xy = ndc.xy * 0.5 + 0.5;
float2 atlasCoords = GetReflectionAtlasCoordsPlanar(PLANAR_SCALE_OFFSET[index], ndc.xy, lod);
color.a = all(abs(samplePosCS.xyz) < samplePosCS.w) ? 1.0 : 0.0;
if (color.a > 0.0)
{
color.rgb = SAMPLE_TEXTURE2D_ARRAY_LOD(_ReflectionAtlas, s_trilinear_clamp_sampler, atlasCoords, sliceIdx, lod).rgb;
color.a *= EdgeSampleFade(positionNDC, ndc.xy);
}
}
else if (cacheType == ENVCACHETYPE_CUBEMAP)
{
float2 atlasCoords = GetReflectionAtlasCoordsCube(CUBE_SCALE_OFFSET[index], texCoord, lod);
color.rgb = SAMPLE_TEXTURE2D_ARRAY_LOD(_ReflectionAtlas, s_trilinear_clamp_sampler, atlasCoords, sliceIdx, lod).rgb;
}
// Planar and Reflection Probes aren't pre-expose, so best to clamp to max16 here in case of inf
color.rgb = ClampToFloat16Max(color.rgb);
color.rgb *= rangeCompressionFactorCompensation;
}
else // SINGLE_PASS_SAMPLE_SKY
{
color.rgb = SampleSkyTexture(texCoord, lod, sliceIdx).rgb;
// Sky isn't pre-expose, so best to clamp to max16 here in case of inf
color.rgb = ClampToFloat16Max(color.rgb);
#ifdef APPLY_FOG_ON_SKY_REFLECTIONS
if (_FogEnabled)
{
float4 fogAttenuation = EvaluateFogForSkyReflections(lightLoopContext.positionWS, texCoord);
color.rgb = color.rgb * fogAttenuation.a + fogAttenuation.rgb;
}
#endif
}
return color;
}
//-----------------------------------------------------------------------------
// Single Pass and Tile Pass
// ----------------------------------------------------------------------------
#ifndef LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
// Calculate the offset in global light index light for current light category
int GetTileOffset(PositionInputs posInput, uint lightCategory)
{
uint2 tileIndex = posInput.tileCoord;
return (tileIndex.y + lightCategory * _NumTileFtplY) * _NumTileFtplX + tileIndex.x;
}
void GetCountAndStartTile(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
{
int tileOffset = GetTileOffset(posInput, lightCategory);
#if defined(UNITY_STEREO_INSTANCING_ENABLED)
// Eye base offset must match code in lightlistbuild.compute
tileOffset += unity_StereoEyeIndex * _NumTileFtplX * _NumTileFtplY * LIGHTCATEGORY_COUNT;
#endif
// The first entry inside a tile is the number of light for lightCategory (thus the +0)
lightCount = g_vLightListTile[LIGHT_DWORD_PER_FPTL_TILE * tileOffset + 0] & 0xffff;
start = tileOffset;
}
#ifdef USE_FPTL_LIGHTLIST
uint GetTileSize()
{
return TILE_SIZE_FPTL;
}
void GetCountAndStart(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
{
GetCountAndStartTile(posInput, lightCategory, start, lightCount);
}
uint FetchIndex(uint tileOffset, uint lightOffset)
{
const uint lightOffsetPlusOne = lightOffset + 1; // Add +1 as first slot is reserved to store number of light
// Light index are store on 16bit
return (g_vLightListTile[LIGHT_DWORD_PER_FPTL_TILE * tileOffset + (lightOffsetPlusOne >> 1)] >> ((lightOffsetPlusOne & 1) * 16)) & 0xffff;
}
#elif defined(USE_CLUSTERED_LIGHTLIST)
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/ClusteredUtils.hlsl"
uint GetTileSize()
{
return TILE_SIZE_CLUSTERED;
}
uint GetLightClusterIndex(uint2 tileIndex, float linearDepth)
{
float logBase = g_fClustBase;
if (g_isLogBaseBufferEnabled)
{
const uint logBaseIndex = GenerateLogBaseBufferIndex(tileIndex, _NumTileClusteredX, _NumTileClusteredY, unity_StereoEyeIndex);
logBase = g_logBaseBuffer[logBaseIndex];
}
return SnapToClusterIdxFlex(linearDepth, logBase, g_isLogBaseBufferEnabled != 0);
}
void UnpackClusterLayeredOffset(uint packedValue, out uint offset, out uint count)
{
offset = packedValue & LIGHT_CLUSTER_PACKING_OFFSET_MASK;
count = packedValue >> LIGHT_CLUSTER_PACKING_OFFSET_BITS;
}
void GetCountAndStartCluster(uint2 tileIndex, uint clusterIndex, uint lightCategory, out uint start, out uint lightCount)
{
int nrClusters = (1 << g_iLog2NumClusters);
const int idx = GenerateLayeredOffsetBufferIndex(lightCategory, tileIndex, clusterIndex, _NumTileClusteredX, _NumTileClusteredY, nrClusters, unity_StereoEyeIndex);
uint dataPair = g_vLayeredOffsetsBuffer[idx];
UnpackClusterLayeredOffset(dataPair, start, lightCount);
}
void GetCountAndStartCluster(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
{
// Note: XR depends on unity_StereoEyeIndex already being defined,
// which means ShaderVariables.hlsl needs to be defined ahead of this!
uint2 tileIndex = posInput.tileCoord;
uint clusterIndex = GetLightClusterIndex(tileIndex, posInput.linearDepth);
GetCountAndStartCluster(tileIndex, clusterIndex, lightCategory, start, lightCount);
}
void GetCountAndStart(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
{
GetCountAndStartCluster(posInput, lightCategory, start, lightCount);
}
uint FetchIndex(uint lightStart, uint lightOffset)
{
return g_vLightListCluster[lightStart + lightOffset];
}
#elif defined(USE_BIG_TILE_LIGHTLIST)
// The "big tile" list contains the number of objects contained within the tile followed by the
// list of object indices. Note that while objects are already sorted by type, we don't know the
// number of each type of objects (e.g. lights), so we should remember to break out of the loop.
// Each light indices are encoded into 16 bits.
void GetBigTileLightCountAndStart(uint tileIndex, out uint lightCount, out uint start)
{
start = tileIndex;
lightCount = g_vBigTileLightList[MAX_NR_BIG_TILE_LIGHTS_PLUS_ONE * tileIndex / 2] & 0xFFFF;
// On Metal for unknow reasons it seems we have bad value sometimes causing freeze / crash. This min here prevent it.
lightCount = min(lightCount, MAX_NR_BIG_TILE_LIGHTS_PLUS_ONE);
}
uint FetchIndex(uint lightStart, uint lightOffset)
{
const uint lightOffsetPlusOne = lightOffset + 1; // Add +1 as first slot is reserved to store number of light
// Light index are store on 16bit
return (g_vBigTileLightList[MAX_NR_BIG_TILE_LIGHTS_PLUS_ONE * lightStart / 2 + (lightOffsetPlusOne >> 1)] >> ((lightOffsetPlusOne & 1) * 16)) & 0xffff;
}
#else
// Fallback case (mainly for raytracing right or for shader stages that don't define the keywords)
uint FetchIndex(uint lightStart, uint lightOffset)
{
return 0;
}
uint GetTileSize()
{
return 1;
}
void GetCountAndStart(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
{
start = 0;
lightCount = 0;
return;
}
#endif // USE_FPTL_LIGHTLIST
#else
uint GetTileSize()
{
return 1;
}
uint FetchIndex(uint lightStart, uint lightOffset)
{
return lightStart + lightOffset;
}
#endif // LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
uint FetchIndexWithBoundsCheck(uint start, uint count, uint i)
{
if (i < count)
{
return FetchIndex(start, i);
}
else
{
return UINT_MAX;
}
}
LightData FetchLight(uint start, uint i)
{
uint j = FetchIndex(start, i);
return _LightDatas[j];
}
LightData FetchLight(uint index)
{
return _LightDatas[index];
}
EnvLightData FetchEnvLight(uint start, uint i)
{
int j = FetchIndex(start, i);
return _EnvLightDatas[j];
}
EnvLightData FetchEnvLight(uint index)
{
return _EnvLightDatas[index];
}
// In the first bits of the target we store the max fade of the contact shadows as a byte.
//By default its 8 bits for the fade and 24 for the mask, please check the LightLoop.cs definitions.
void UnpackContactShadowData(uint contactShadowData, out float fade, out uint mask)
{
fade = float(contactShadowData >> CONTACT_SHADOW_MASK_BITS) / ((float)CONTACT_SHADOW_FADE_MASK);
mask = contactShadowData & CONTACT_SHADOW_MASK_MASK; // store only the first 24 bits which represent
}
uint PackContactShadowData(float fade, uint mask)
{
uint fadeAsByte = (uint(saturate(fade) * CONTACT_SHADOW_FADE_MASK) << CONTACT_SHADOW_MASK_BITS);
return fadeAsByte | mask;
}
// We always fetch the screen space shadow texture to reduce the number of shader variant, overhead is negligible,
// it is a 1x1 white texture if deferred directional shadow and/or contact shadow are disabled
// We perform a single featch a the beginning of the lightloop
void InitContactShadow(PositionInputs posInput, inout LightLoopContext context)
{
// Note: When we ImageLoad outside of texture size, the value returned by Load is 0 (Note: On Metal maybe it clamp to value of texture which is also fine)
// We use this property to have a neutral value for contact shadows that doesn't consume a sampler and work also with compute shader (i.e use ImageLoad)
// We store inverse contact shadow so neutral is white. So either we sample inside or outside the texture it return 1 in case of neutral
uint packedContactShadow = LOAD_TEXTURE2D_X(_ContactShadowTexture, posInput.positionSS).x;
UnpackContactShadowData(packedContactShadow, context.contactShadowFade, context.contactShadow);
}
void InvalidateConctactShadow(PositionInputs posInput, inout LightLoopContext context)
{
context.contactShadowFade = 0.0;
context.contactShadow = 0;
}
float GetContactShadow(LightLoopContext lightLoopContext, int contactShadowMask, float rayTracedShadow)
{
bool occluded = (lightLoopContext.contactShadow & contactShadowMask) != 0;
return 1.0 - occluded * lerp(lightLoopContext.contactShadowFade, 1.0, rayTracedShadow) * _ContactShadowOpacity;
}
float GetScreenSpaceShadow(PositionInputs posInput, uint shadowIndex)
{
uint slot = shadowIndex / 4;
uint channel = shadowIndex & 0x3;
return LOAD_TEXTURE2D_ARRAY(_ScreenSpaceShadowsTexture, posInput.positionSS, INDEX_TEXTURE2D_ARRAY_X(slot))[channel];
}
float2 GetScreenSpaceShadowArea(PositionInputs posInput, uint shadowIndex)
{
uint slot = shadowIndex / 4;
uint channel = shadowIndex & 0x3;
return float2(LOAD_TEXTURE2D_ARRAY(_ScreenSpaceShadowsTexture, posInput.positionSS, INDEX_TEXTURE2D_ARRAY_X(slot))[channel], LOAD_TEXTURE2D_ARRAY(_ScreenSpaceShadowsTexture, posInput.positionSS, INDEX_TEXTURE2D_ARRAY_X(slot))[channel + 1]);
}
float3 GetScreenSpaceColorShadow(PositionInputs posInput, int shadowIndex)
{
float4 res = LOAD_TEXTURE2D_ARRAY(_ScreenSpaceShadowsTexture, posInput.positionSS, INDEX_TEXTURE2D_ARRAY_X(shadowIndex & SCREEN_SPACE_SHADOW_INDEX_MASK));
return (SCREEN_SPACE_COLOR_SHADOW_FLAG & shadowIndex) ? res.xyz : res.xxx;
}
#endif // UNITY_LIGHT_LOOP_DEF_INCLUDED