浏览代码

Added support for choosing shadow algorithms. Each algorithm can have up to 8 variants.

Added VSM, EVSM and MSM shadowmaps.
Added custom editor to additional light data for drawing shadow specific UI.
/main
uygar 8 年前
当前提交
94d9ef0a
共有 15 个文件被更改,包括 1559 次插入188 次删除
  1. 111
      Assets/ScriptableRenderPipeline/AdditionalLightData.cs
  2. 22
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.cs
  3. 8
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl
  4. 541
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.cs
  5. 52
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.hlsl
  6. 202
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithms.hlsl
  7. 30
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithmsCustom.hlsl
  8. 318
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs
  9. 26
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs.hlsl
  10. 17
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowContext.hlsl
  11. 49
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowDispatch.hlsl
  12. 221
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowSampling.hlsl
  13. 15
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowUtilities.cs
  14. 126
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowMoments.hlsl
  15. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowMoments.hlsl.meta

111
Assets/ScriptableRenderPipeline/AdditionalLightData.cs


using UnityEngine;
using System.Collections;
namespace UnityEngine.Experimental.Rendering
{
public enum LightArchetype {Punctual, Rectangle, Line};

[Range(0.0f, 20.0f)]
public float areaLightWidth = 0.0f;
// shadow related parameters
[System.Serializable]
public struct ShadowData
{
public int format;
public int[] data;
};
[HideInInspector, SerializeField] private int shadowAlgorithm;
[HideInInspector, SerializeField] private int shadowVariant;
[HideInInspector, SerializeField] private ShadowData shadowData;
[HideInInspector, SerializeField] private ShadowData[] shadowDatas = new ShadowData[0];
public void GetShadowAlgorithm( out int algorithm, out int variant ) { algorithm = shadowAlgorithm; variant = shadowVariant; }
public void SetShadowAlgorithm( int algorithm, int variant, int format, int[] data )
{
shadowAlgorithm = algorithm;
shadowVariant = variant;
shadowData.format = format;
shadowData.data = data;
int idx = FindShadowData( format );
if( idx < 0 )
{
idx = shadowDatas.Length;
ShadowData[] tmp = new ShadowData[idx+1];
for( int i = 0; i < idx; ++i )
tmp[i] = shadowDatas[i];
shadowDatas = tmp;
}
shadowDatas[idx].format = format;
shadowDatas[idx].data = data != null ? data : new int[0];
UnityEditor.EditorUtility.SetDirty( this );
}
// Load a specific shadow data. Returns null if requested data is not present.
public int[] GetShadowData( int shadowDataFormat )
{
if( shadowData.format == shadowDataFormat )
return shadowData.data;
int idx = FindShadowData( shadowDataFormat );
return idx >= 0 ? shadowDatas[idx].data : null;
}
// Returns the currently set shadow data and format. Can return null.
public int[] GetShadowData( out int shadowDataFormat )
{
shadowDataFormat = shadowData.format;
return shadowData.data;
}
public void CompactShadowData()
{
shadowDatas = new ShadowData[0];
UnityEditor.EditorUtility.SetDirty(this);
}
private int FindShadowData( int shadowDataFormat )
{
for( int i = 0; i < shadowDatas.Length; ++i )
{
if( shadowDatas[i].format == shadowDataFormat )
return i;
}
return -1;
}
}
[UnityEditor.CustomEditor(typeof(AdditionalLightData))]
[UnityEditor.CanEditMultipleObjects]
public class AdditionalLightDataEditor : UnityEditor.Editor
{
static HDPipeline.ShadowExp.ShadowRegistry m_ShadowRegistry;
UnityEditor.SerializedProperty m_ShadowAlgorithm;
UnityEditor.SerializedProperty m_ShadowVariant;
UnityEditor.SerializedProperty m_ShadowData;
UnityEditor.SerializedProperty m_ShadowDatas;
public static void SetRegistry( HDPipeline.ShadowExp.ShadowRegistry registry ) { m_ShadowRegistry = registry; }
void OnEnable()
{
m_ShadowAlgorithm = serializedObject.FindProperty( "shadowAlgorithm" );
m_ShadowVariant = serializedObject.FindProperty( "shadowVariant" );
m_ShadowData = serializedObject.FindProperty( "shadowData" );
m_ShadowDatas = serializedObject.FindProperty( "shadowDatas" );
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if( m_ShadowRegistry == null )
return;
AdditionalLightData ald = (AdditionalLightData) target;
if( ald == null )
return;
UnityEditor.EditorGUI.BeginChangeCheck();
m_ShadowRegistry.Draw( ald.gameObject.GetComponent<Light>() );
serializedObject.Update();
serializedObject.ApplyModifiedProperties();
if( UnityEditor.EditorGUI.EndChangeCheck() )
{
//UnityEditor.EditorUtility.SetDirty( ald ); // <- doesn't work for some reason
UnityEditor.SceneManagement.EditorSceneManager.MarkAllScenesDirty();
UnityEditor.SceneView.RepaintAll();
}
}
}
}

22
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.cs


//#define SHADOWS_ENABLED
//#define SHADOWS_FIXSHADOWIDX
#define SHADOWS_ENABLED
#define SHADOWS_FIXSHADOWIDX
using UnityEngine.Rendering;
using System.Collections.Generic;
using System;

public ShadowSetup( ShadowSettings shadowSettings, out IShadowManager shadowManager )
{
s_ShadowDataBuffer = new ComputeBuffer( k_MaxShadowDataSlots, System.Runtime.InteropServices.Marshal.SizeOf( typeof( ShadowExp.ShadowData ) ) );
s_ShadowPayloadBuffer = new ComputeBuffer( k_MaxShadowDataSlots * k_MaxPayloadSlotsPerShadowData, System.Runtime.InteropServices.Marshal.SizeOf( typeof( ShadowExp.ShadowData ) ) );
s_ShadowPayloadBuffer = new ComputeBuffer( k_MaxShadowDataSlots * k_MaxPayloadSlotsPerShadowData, System.Runtime.InteropServices.Marshal.SizeOf( typeof( ShadowExp.ShadowPayload ) ) );
ShadowAtlas.AtlasInit atlasInit;
atlasInit.baseInit.width = (uint) shadowSettings.shadowAtlasWidth;
atlasInit.baseInit.height = (uint) shadowSettings.shadowAtlasHeight;

atlasInit.baseInit.samplerState = SamplerState.Default();
atlasInit.baseInit.comparisonSamplerState = ComparisonSamplerState.Default();
atlasInit.baseInit.clearColor = new Vector4( 0.0f, 0.0f, 0.0f, 0.0f );
atlasInit.baseInit.maxPayloadCount = 0;
atlasInit.baseInit.shadowSupport = ShadowmapBase.ShadowSupport.Directional;

var atlasInit2 = atlasInit;
atlasInit2.baseInit.shadowSupport = ShadowmapBase.ShadowSupport.Point | ShadowmapBase.ShadowSupport.Spot;
m_Shadowmaps = new ShadowmapBase[] { new ShadowExp.ShadowAtlas( ref atlasInit ), new ShadowExp.ShadowAtlas( ref atlasInit2 ) };
var varianceInit = atlasInit;
varianceInit.baseInit.shadowmapFormat = RenderTextureFormat.ARGBFloat;
m_Shadowmaps = new ShadowmapBase[] { new ShadowExp.ShadowVariance( ref varianceInit ), new ShadowExp.ShadowAtlas( ref atlasInit ), new ShadowExp.ShadowAtlas( ref atlasInit2 ) };
//m_Shadowmaps = new ShadowmapBase[] { new ShadowExp.ShadowAtlas( ref atlasInit ), new ShadowExp.ShadowAtlas( ref atlasInit2 ) };
ShadowContext.SyncDel syncer = (ShadowContext sc) =>
{

uint offset, count;
RenderTargetIdentifier[] tex;
sc.GetTex2DArrays( out tex, out offset, out count );
cb.SetGlobalTexture( "_ShadowmapExp_Dir", tex[0] );
cb.SetGlobalTexture( "_ShadowmapExp_PointSpot", tex[1] );
cb.SetGlobalTexture( "_ShadowmapExp_Dir_VSM", tex[0] );
cb.SetGlobalTexture( "_ShadowmapExp_Dir", tex[1] );
cb.SetGlobalTexture( "_ShadowmapExp_PointSpot", tex[2] );
// TODO: Currently samplers are hard coded in ShadowContext.hlsl, so we can't really set them here
};

(m_Shadowmaps[0] as ShadowAtlas).Dispose();
(m_Shadowmaps[1] as ShadowAtlas).Dispose();
(m_Shadowmaps[2] as ShadowAtlas).Dispose();
m_Shadowmaps = null;
}
m_ShadowMgr = null;

public override void Cleanup()
{
DeinitShadowSystem();
#if UNITY_EDITOR
UnityEditor.SceneView.onSceneGUIDelegate -= OnSceneGUI;
#endif

8
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl


[branch] if (lightData.shadowIndex >= 0 && illuminance > 0.0)
{
#ifdef SHADOWS_USE_SHADOWCTXT
float shadow = GetDirectionalShadowAttenuation(lightLoopContext.shadowContext, positionWS, lightData.shadowIndex, L, posInput.unPositionSS);
float shadow = GetDirectionalShadowAttenuation(lightLoopContext.shadowContext, positionWS, bsdfData.normalWS, lightData.shadowIndex, L, posInput.unPositionSS);
#else
float shadow = GetDirectionalShadowAttenuation(lightLoopContext, positionWS, lightData.shadowIndex, L, posInput.unPositionSS);
#endif

// TODO: factor out the biased position?
float3 biasedPositionWS = positionWS + bsdfData.normalWS * bsdfData.thickness;
#ifdef SHADOWS_USE_SHADOWCTXT
float shadow = GetDirectionalShadowAttenuation(lightLoopContext.shadowContext, biasedPositionWS, lightData.shadowIndex, L, posInput.unPositionSS);
float shadow = GetDirectionalShadowAttenuation(lightLoopContext.shadowContext, biasedPositionWS, bsdfData.normalWS, lightData.shadowIndex, L, posInput.unPositionSS);
#else
float shadow = GetDirectionalShadowAttenuation(lightLoopContext, biasedPositionWS, lightData.shadowIndex, L, posInput.unPositionSS);
#endif

{
float3 offset = float3(0.0, 0.0, 0.0); // GetShadowPosOffset(nDotL, normal);
#ifdef SHADOWS_USE_SHADOWCTXT
float shadow = GetPunctualShadowAttenuation(lightLoopContext.shadowContext, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
float shadow = GetPunctualShadowAttenuation(lightLoopContext.shadowContext, positionWS + offset, bsdfData.normalWS, lightData.shadowIndex, L, posInput.unPositionSS);
#else
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#endif

float3 biasedPositionWS = positionWS + bsdfData.normalWS * bsdfData.thickness;
float3 offset = float3(0.0, 0.0, 0.0); // GetShadowPosOffset(nDotL, normal);
#ifdef SHADOWS_USE_SHADOWCTXT
float shadow = GetPunctualShadowAttenuation(lightLoopContext.shadowContext, biasedPositionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
float shadow = GetPunctualShadowAttenuation(lightLoopContext.shadowContext, biasedPositionWS + offset, bsdfData.normalWS, lightData.shadowIndex, L, posInput.unPositionSS);
#else
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, biasedPositionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#endif

541
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.cs


using ShadowDataVector = VectorArray<ShadowData>;
using ShadowPayloadVector = VectorArray<ShadowPayload>;
using ShadowIndicesVector = VectorArray<int>;
using ShadowAlgoVector = VectorArray<GPUShadowAlgorithm>;
// Standard shadow map atlas implementation using one large shadow map
public class ShadowAtlas : ShadowmapBase, IDisposable

protected uint[] m_TmpWidths = new uint[ShadowmapBase.ShadowRequest.k_MaxFaceCount];
protected uint[] m_TmpHeights = new uint[ShadowmapBase.ShadowRequest.k_MaxFaceCount];
protected Vector4[] m_TmpSplits = new Vector4[k_MaxCascadesInShader];
protected ShadowAlgoVector m_SupportedAlgorithms = new ShadowAlgoVector( 0, false );
protected struct Key
{

protected struct Data
{
public FrameId frameId;
public int contentHash;
public uint slice;
public Rect viewport;
public Matrix4x4 view;
public Matrix4x4 proj;
public Vector4 lightDir;
public ShadowSplitData splitData;
public FrameId frameId;
public int contentHash;
public GPUShadowAlgorithm shadowAlgo;
public uint slice;
public Rect viewport;
public Matrix4x4 view;
public Matrix4x4 proj;
public Vector4 lightDir;
public ShadowSplitData splitData;
public bool IsValid() { return viewport.width > 0 && viewport.height > 0; }
}

public Vector3 cascadeRatios; // cascade split ratios
}
// UI stuff
protected struct ValRange
{
GUIContent Name;
float ValMin;
float ValDef;
float ValMax;
float ValScale;
public ValRange( string name, float valMin, float valDef, float valMax, float valScale ) { Name = new GUIContent( name ); ValMin = valMin; ValDef = valDef; ValMax = valMax; ValScale = valScale; }
public void Slider( ref int currentVal ) { currentVal = ShadowUtils.Asint( ValScale * UnityEditor.EditorGUILayout.Slider( Name, ShadowUtils.Asfloat( currentVal ) / ValScale, ValMin, ValMax ) ); }
public int Default() { return ShadowUtils.Asint( ValScale * ValDef ); }
}
readonly ValRange m_DefPCF_DepthBias = new ValRange( "Depth Bias", 0.0f, 0.05f, 1.0f, 00.1f );
readonly ValRange m_DefPCF_FilterSize = new ValRange( "Filter Size", 1.0f, 1.0f, 10.0f, 1.0f );
public ShadowAtlas( ref AtlasInit init ) : base( ref init.baseInit )
{

Initialize( init );
}
override protected void Register( GPUShadowType type, ShadowRegistry registry )
{
m_SupportedAlgorithms.Reserve( 2 );
m_SupportedAlgorithms.AddUnchecked( GPUShadowAlgorithm.PCF_1tap );
m_SupportedAlgorithms.AddUnchecked( GPUShadowAlgorithm.PCF_9tap );
ShadowRegistry.VariantDelegate del = ( Light l, ShadowAlgorithm dataAlgorithm, ShadowVariant dataVariant, ref int[] dataBlock ) =>
{
CheckDataIntegrity( dataAlgorithm, dataVariant, ref dataBlock );
m_DefPCF_DepthBias.Slider( ref dataBlock[0] );
if( dataVariant == ShadowVariant.V1 )
m_DefPCF_FilterSize.Slider( ref dataBlock[1] );
};
registry.Register( type, ShadowAlgorithm.PCF, "Percentage Closer Filtering (PCF)",
new ShadowVariant[]{ ShadowVariant.V0, ShadowVariant.V1 }, new string[]{"1 tap", "9 tap adaptive" }, new ShadowRegistry.VariantDelegate[] { del, del } );
}
// returns true if the original data passed integrity checks, false if the data had to be modified
virtual protected bool CheckDataIntegrity( ShadowAlgorithm algorithm, ShadowVariant variant, ref int[] dataBlock )
{
if( algorithm != ShadowAlgorithm.PCF || (variant != ShadowVariant.V0 && variant != ShadowVariant.V1) )
return true;
const int k_BlockSize = 2;
if( dataBlock == null || dataBlock.Length != k_BlockSize )
{
// set defaults
dataBlock = new int[k_BlockSize];
dataBlock[0] = m_DefPCF_DepthBias.Default();
dataBlock[1] = m_DefPCF_FilterSize.Default();
return false;
}
return true;
}
public void Initialize( AtlasInit init )
{
m_ShaderKeyword = init.shaderKeyword;

override public bool Reserve( FrameId frameId, ref ShadowData shadowData, ShadowRequest sr, uint[] widths, uint[] heights, ref VectorArray<ShadowData> entries, ref VectorArray<ShadowPayload> payload, VisibleLight[] lights )
{
if( m_FrameId.frameCount != frameId.frameCount )
m_ActiveEntriesCount = 0;
m_FrameId = frameId;
uint algoIdx;
GPUShadowAlgorithm shadowAlgo = sr.shadowAlgorithm;
if( !m_SupportedAlgorithms.FindFirst( out algoIdx, ref shadowAlgo ) )
return false;
ShadowData sd = shadowData;
ShadowData dummy = new ShadowData();

uint facemask = sr.facemask;
uint bit = 1;
int resIdx = 0;
bool multiFace= sr.shadowType != GPUShadowType.Spot;
entries.Reserve( 6 );
const uint k_MaxShadowDatasPerLight = 7; // 1 shared ShadowData and up to 6 faces for point lights
entries.Reserve( k_MaxShadowDatasPerLight );
if( multiFace )
{
// For lights with multiple faces, the first shadow data contains
// per light information, so not all fields contain valid data.
// Shader code must make sure to read per face data from per face entries.
sd.texelSizeRcp = new Vector2( m_WidthRcp, m_HeightRcp );
sd.PackShadowType( sr.shadowType, sr.shadowAlgorithm );
sd.payloadOffset = payload.Count();
entries.AddUnchecked( sd );
key.shadowDataIdx++;
}
payload.Resize( payload.Count() + ReservePayload( sr ) );
while ( facecnt > 0 )
{
if( (bit & facemask) != 0 )

else
vp = Matrix4x4.identity; // should never happen, though
// write :(
ce.current.shadowAlgo = shadowAlgo;
m_EntryCache[ceIdx] = ce;
sd.worldToShadow = vp.transpose; // apparently we need to transpose matrices that are sent to HLSL

sd.shadowType = sr.shadowType;
sd.payloadOffset = payload.Count();
entries.AddUnchecked(sd);
sd.PackShadowType( sr.shadowType, sr.shadowAlgorithm );
sd.payloadOffset = originalPayloadCount;
entries.AddUnchecked( sd );
resIdx++;
facecnt--;

bit <<= 1;
}
if (sr.shadowType == GPUShadowType.Directional)
WritePerLightPayload( ref lights[sr.index], sr, ref sd, ref payload, ref originalPayloadCount );
return true;
}
// Returns how many entries will be written into the payload buffer per light.
virtual protected uint ReservePayload( ShadowRequest sr )
{
uint payloadSize = sr.shadowType == GPUShadowType.Directional ? k_MaxCascadesInShader : 0;
payloadSize += ShadowUtils.ExtractAlgorithm( sr.shadowAlgorithm ) == ShadowAlgorithm.PCF ? 1u : 0;
return payloadSize;
}
// Writes additional per light data into the payload vector. Make sure to call base.WritePerLightPayload first.
virtual protected void WritePerLightPayload( ref VisibleLight light, ShadowRequest sr, ref ShadowData sd, ref ShadowPayloadVector payload, ref uint payloadOffset )
{
ShadowPayload sp = new ShadowPayload();
if( sr.shadowType == GPUShadowType.Directional )
ShadowPayload sp = new ShadowPayload();
payload.Reserve( k_MaxCascadesInShader );
for( uint i = 0; i < k_MaxCascadesInShader; i++ )
for( uint i = 0; i < k_MaxCascadesInShader; i++, payloadOffset++ )
payload.AddUnchecked( sp );
payload[payloadOffset] = sp;
ShadowAlgorithm algo; ShadowVariant vari;
ShadowUtils.Unpack( sr.shadowAlgorithm, out algo, out vari );
if( algo == ShadowAlgorithm.PCF )
{
AdditionalLightData ald = light.light.GetComponent<AdditionalLightData>();
if( !ald )
return;
return true;
int shadowDataFormat;
int[] shadowData = ald.GetShadowData( out shadowDataFormat );
if( !CheckDataIntegrity( algo, vari, ref shadowData ) )
{
ald.SetShadowAlgorithm( (int)algo, (int)vari, shadowDataFormat, shadowData );
Debug.Log( "Fixed up shadow data for algorithm " + algo + ", variant " + vari );
}
switch( sr.shadowAlgorithm )
{
case GPUShadowAlgorithm.PCF_1tap:
case GPUShadowAlgorithm.PCF_9tap:
{
sp.Set( shadowData[0] | (SystemInfo.usesReversedZBuffer ? 1 : 0), shadowData[1], 0, 0 );
payload[payloadOffset] = sp;
payloadOffset++;
}
break;
}
}
}
override public bool ReserveFinalize( FrameId frameId, ref VectorArray<ShadowData> entries, ref VectorArray<ShadowPayload> payload )

if( curSlice != uint.MaxValue )
{
PostUpdate( frameId, cb, curSlice );
PostUpdate( frameId, cb, curSlice, lights );
}
curSlice = entrySlice;
PreUpdate( frameId, cb, curSlice );

// post update
var cblast = new CommandBuffer();
PostUpdate( frameId, cblast, curSlice );
PostUpdate( frameId, cblast, curSlice, lights );
if( !string.IsNullOrEmpty( m_ShaderKeyword ) )
{
cblast.name = "Shadowmap.DisableShaderKeyword";

profilingSample.Dispose();
}
virtual protected void PostUpdate( FrameId frameId, CommandBuffer cb, uint rendertargetSlice )
virtual protected void PostUpdate( FrameId frameId, CommandBuffer cb, uint rendertargetSlice, VisibleLight[] lights )
{
if( !IsNativeDepth() )
cb.ReleaseTemporaryRT( m_TempDepthId );

// -------------------------------------------------------------------------------------------------------------------------------------------------
//
// ShadowVariance
//
// -------------------------------------------------------------------------------------------------------------------------------------------------
// Shadowmap supporting various flavors of variance shadow maps.
public class ShadowVariance : ShadowAtlas
{
protected const int k_MomentBlurThreadsPerWorkgroup = 16;
protected const int k_BlurKernelMinSize = 3;
protected const int k_BlurKernelDefSize = (7 - k_BlurKernelMinSize) / 2;
protected const int k_BlurKernelMaxSize = 17;
protected const int k_BlurKernelCount = k_BlurKernelMaxSize / 2;
protected ComputeShader m_MomentBlurCS;
protected int[] m_KernelVSM = new int[k_BlurKernelCount];
protected int[] m_KernelEVSM_2 = new int[k_BlurKernelCount];
protected int[] m_KernelEVSM_4 = new int[k_BlurKernelCount];
protected int[] m_KernelMSM = new int[k_BlurKernelCount];
protected float[][] m_BlurWeights = new float[k_BlurKernelCount][];
protected int m_SampleCount;
// default values
readonly ValRange m_DefVSM_LightLeakBias = new ValRange( "Light leak bias" , 0.0f, 0.5f , 0.99f , 1.0f );
readonly ValRange m_DefVSM_VarianceBias = new ValRange( "Variance bias" , 0.0f, 0.1f , 1.0f , 0.01f );
readonly ValRange m_DefEVSM_LightLeakBias = new ValRange( "Light leak bias" , 0.0f, 0.0f , 0.99f , 1.0f );
readonly ValRange m_DefEVSM_VarianceBias = new ValRange( "Variance bias" , 0.0f, 0.1f , 1.0f , 0.01f );
readonly ValRange m_DefEVSM_PosExponent = new ValRange( "Positive Exponent" , 1.0f, 1.0f , 42.0f , 1.0f );
readonly ValRange m_DefEVSM_NegExponent = new ValRange( "Negative Exponent" , 1.0f, 1.0f , 42.0f , 1.0f );
readonly ValRange m_DefMSM_LightLeakBias = new ValRange( "Light leak bias" , 0.0f, 0.5f , 0.99f , 1.0f );
readonly ValRange m_DefMSM_MomentBias = new ValRange( "Moment Bias" , 0.0f, 0.3f , 1.0f , 0.00003f);
readonly ValRange m_DefMSM_DepthBias = new ValRange( "Depth Bias" , 0.0f, 0.1f , 1.0f , 0.1f );
public ShadowVariance( ref AtlasInit init ) : base( ref init )
{
m_Shadowmap.enableRandomWrite = true;
m_SampleCount = 1; // TODO: Unity can't bind msaa rts as textures, yet, so this has to remain 1 for now
m_MomentBlurCS = Resources.Load<ComputeShader>( "ShadowBlurMoments" );
if( m_MomentBlurCS )
{
for( int i = 0, blurSize = 3; i < k_BlurKernelCount; ++i, blurSize += 2 )
{
m_KernelVSM[i] = m_MomentBlurCS.FindKernel( "main_VSM_" + blurSize );
m_KernelEVSM_2[i] = m_MomentBlurCS.FindKernel( "main_EVSM_2_" + blurSize );
m_KernelEVSM_4[i] = m_MomentBlurCS.FindKernel( "main_EVSM_4_" + blurSize );
m_KernelMSM[i] = m_MomentBlurCS.FindKernel( "main_MSM_" + blurSize );
m_BlurWeights[i] = new float[2+i];
FillBlurWeights( i );
}
}
// normalize blur weights
for( int i = 0; i < k_BlurKernelCount; ++i )
{
float weightSum = 0.0f;
for( int j = 0; j < m_BlurWeights[i].Length; ++j )
weightSum += m_BlurWeights[i][j];
weightSum = 1.0f / (2.0f * weightSum - m_BlurWeights[i][0]);
for( int j = 0; j < m_BlurWeights[i].Length; ++j )
m_BlurWeights[i][j] *= weightSum;
}
}
private void FillBlurWeights( int idx )
{
if( idx == 0 )
{
m_BlurWeights[0][0] = 2.0f;
m_BlurWeights[0][1] = 1.0f;
return;
}
float[] prev = m_BlurWeights[idx-1];
float[] cur = m_BlurWeights[idx];
int prevSize = prev.Length;
for( int i = 0; i < prevSize; ++i )
{
cur[i] = prev[Math.Abs( i-1 )] + prev[i] * 2.0f + (i == (prevSize-1) ? 0.0f : prev[i+1]);
}
cur[cur.Length-1] = 1.0f;
}
private void BlurSlider( ref int currentVal )
{
currentVal = k_BlurKernelMinSize + currentVal * 2;
currentVal = (int) Math.Round( UnityEditor.EditorGUILayout.Slider( "Blur Size", currentVal, k_BlurKernelMinSize, k_BlurKernelMaxSize ) );
currentVal = (currentVal - k_BlurKernelMinSize) / 2;
}
override protected void Register( GPUShadowType type, ShadowRegistry registry )
{
m_SupportedAlgorithms.Reserve( 5 );
m_SupportedAlgorithms.AddUnchecked( GPUShadowAlgorithm.VSM );
m_SupportedAlgorithms.AddUnchecked( GPUShadowAlgorithm.EVSM_2 );
m_SupportedAlgorithms.AddUnchecked( GPUShadowAlgorithm.EVSM_4 );
m_SupportedAlgorithms.AddUnchecked( GPUShadowAlgorithm.MSM_Ham );
m_SupportedAlgorithms.AddUnchecked( GPUShadowAlgorithm.MSM_Haus );
ShadowRegistry.VariantDelegate vsmDel = ( Light l, ShadowAlgorithm dataAlgorithm, ShadowVariant dataVariant, ref int[] dataBlock ) =>
{
CheckDataIntegrity( dataAlgorithm, dataVariant, ref dataBlock );
m_DefVSM_LightLeakBias.Slider( ref dataBlock[0] );
m_DefVSM_VarianceBias.Slider( ref dataBlock[1] );
BlurSlider( ref dataBlock[2] );
};
ShadowRegistry.VariantDelegate evsmDel = ( Light l, ShadowAlgorithm dataAlgorithm, ShadowVariant dataVariant, ref int[] dataBlock ) =>
{
CheckDataIntegrity( dataAlgorithm, dataVariant, ref dataBlock );
m_DefEVSM_LightLeakBias.Slider( ref dataBlock[0] );
m_DefEVSM_VarianceBias.Slider( ref dataBlock[1] );
m_DefEVSM_PosExponent.Slider( ref dataBlock[2] );
if( dataVariant == ShadowVariant.V1 )
{
m_DefEVSM_NegExponent.Slider( ref dataBlock[3] );
}
BlurSlider( ref dataBlock[4] );
};
ShadowRegistry.VariantDelegate msmDel = ( Light l, ShadowAlgorithm dataAlgorithm, ShadowVariant dataVariant, ref int[] dataBlock ) =>
{
CheckDataIntegrity( dataAlgorithm, dataVariant, ref dataBlock );
m_DefMSM_LightLeakBias.Slider( ref dataBlock[0] );
m_DefMSM_MomentBias.Slider( ref dataBlock[1] );
m_DefMSM_DepthBias.Slider( ref dataBlock[2] );
BlurSlider( ref dataBlock[3] );
};
registry.Register( type, ShadowAlgorithm.VSM, "Variance shadow map (VSM)",
new ShadowVariant[] { ShadowVariant.V0 }, new string[] { "2 moments" }, new ShadowRegistry.VariantDelegate[] { vsmDel } );
registry.Register( type, ShadowAlgorithm.EVSM, "Exponential variance shadow map (EVSM)",
new ShadowVariant[] { ShadowVariant.V0, ShadowVariant.V1 }, new string[] { "2 moments", "4 moments" }, new ShadowRegistry.VariantDelegate[] { evsmDel, evsmDel } );
registry.Register( type, ShadowAlgorithm.MSM, "Momentum shadow map (MSM)",
new ShadowVariant[] { ShadowVariant.V0, ShadowVariant.V1 }, new string[] { "Hamburg", "Hausdorff" }, new ShadowRegistry.VariantDelegate[] { msmDel, msmDel } );
}
override protected bool CheckDataIntegrity( ShadowAlgorithm algorithm, ShadowVariant variant, ref int[] dataBlock )
{
switch( algorithm )
{
case ShadowAlgorithm.VSM:
{
const int k_BlockSize = 3;
if( dataBlock == null || dataBlock.Length != k_BlockSize )
{
// set defaults
dataBlock = new int[k_BlockSize];
dataBlock[0] = m_DefVSM_LightLeakBias.Default();
dataBlock[1] = m_DefVSM_VarianceBias.Default();
dataBlock[2] = k_BlurKernelDefSize;
return false;
}
return true;
}
case ShadowAlgorithm.EVSM:
{
const int k_BlockSize = 5;
if( dataBlock == null || dataBlock.Length != k_BlockSize )
{
// set defaults
dataBlock = new int[k_BlockSize];
dataBlock[0] = m_DefEVSM_LightLeakBias.Default();
dataBlock[1] = m_DefEVSM_VarianceBias.Default();
dataBlock[2] = m_DefEVSM_PosExponent.Default();
dataBlock[3] = m_DefEVSM_NegExponent.Default();
dataBlock[4] = k_BlurKernelDefSize;
return false;
}
return true;
}
case ShadowAlgorithm.MSM:
{
const int k_BlockSize = 4;
if( dataBlock == null || dataBlock.Length != k_BlockSize )
{
// set defaults
dataBlock = new int[k_BlockSize];
dataBlock[0] = m_DefMSM_LightLeakBias.Default();
dataBlock[1] = m_DefMSM_MomentBias.Default();
dataBlock[2] = m_DefMSM_DepthBias.Default();
dataBlock[3] = k_BlurKernelDefSize;
return false;
}
return true;
}
default: return base.CheckDataIntegrity( algorithm, variant, ref dataBlock );
}
}
override protected uint ReservePayload( ShadowRequest sr )
{
uint cnt = base.ReservePayload( sr );
switch( sr.shadowAlgorithm )
{
case GPUShadowAlgorithm.VSM : return cnt + 1;
case GPUShadowAlgorithm.EVSM_2 :
case GPUShadowAlgorithm.EVSM_4 : return cnt + 1;
case GPUShadowAlgorithm.MSM_Ham :
case GPUShadowAlgorithm.MSM_Haus: return cnt + 1;
default: return cnt;
}
}
// Writes additional per light data into the payload vector. Make sure to call base.WritePerLightPayload first.
override protected void WritePerLightPayload( ref VisibleLight light, ShadowRequest sr, ref ShadowData sd, ref ShadowPayloadVector payload, ref uint payloadOffset )
{
base.WritePerLightPayload( ref light, sr, ref sd, ref payload, ref payloadOffset );
AdditionalLightData ald = light.light.GetComponent<AdditionalLightData>();
if( !ald )
return;
ShadowPayload sp = new ShadowPayload();
int shadowDataFormat;
int[] shadowData = ald.GetShadowData( out shadowDataFormat );
if( shadowData == null )
return;
ShadowAlgorithm algo;
ShadowVariant vari;
ShadowUtils.Unpack( sr.shadowAlgorithm, out algo, out vari );
CheckDataIntegrity( algo, vari, ref shadowData );
switch (sr.shadowAlgorithm)
{
case GPUShadowAlgorithm.VSM:
{
sp.p0 = shadowData[0];
sp.p1 = shadowData[1];
payload[payloadOffset] = sp;
payloadOffset++;
}
break;
case GPUShadowAlgorithm.EVSM_2:
case GPUShadowAlgorithm.EVSM_4:
{
sp.p0 = shadowData[0];
sp.p1 = shadowData[1];
sp.p2 = shadowData[2];
sp.p3 = shadowData[3];
payload[payloadOffset] = sp;
payloadOffset++;
}
break;
case GPUShadowAlgorithm.MSM_Ham:
case GPUShadowAlgorithm.MSM_Haus:
{
sp.Set( shadowData[0], shadowData[1], shadowData[2] | (SystemInfo.usesReversedZBuffer ? 1 : 0), 0 );
payload[payloadOffset] = sp;
payloadOffset++;
}
break;
}
}
override protected void PreUpdate( FrameId frameId, CommandBuffer cb, uint rendertargetSlice )
{
cb.SetRenderTarget( m_ShadowmapId, 0, (CubemapFace) 0, (int) rendertargetSlice );
cb.GetTemporaryRT( m_TempDepthId, (int) m_Width, (int) m_Height, (int) m_ShadowmapBits, FilterMode.Bilinear, RenderTextureFormat.Shadowmap, RenderTextureReadWrite.Default, m_SampleCount );
cb.SetRenderTarget( new RenderTargetIdentifier( m_TempDepthId ) );
cb.ClearRenderTarget( true, true, m_ClearColor );
}
protected override void PostUpdate( FrameId frameId, CommandBuffer cb, uint rendertargetSlice, VisibleLight[] lights )
{
cb.name = "VSM conversion";
if ( rendertargetSlice == uint.MaxValue )
{
base.PostUpdate( frameId, cb, rendertargetSlice, lights );
return;
}
base.PostUpdate( frameId, cb, rendertargetSlice, lights );
uint cnt = m_EntryCache.Count();
uint i = 0;
while( i < cnt && m_EntryCache[i].current.slice < rendertargetSlice )
{
i++;
}
if( i >= cnt || m_EntryCache[i].current.slice > rendertargetSlice )
return;
int kernelIdx = 2;
int currentKernel = 0;
for( int j = 0; j < k_BlurKernelCount; ++j )
{
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelVSM[j] , "depthTex" , new RenderTargetIdentifier( m_TempDepthId ) );
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelVSM[j] , "outputTex", m_ShadowmapId );
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelEVSM_2[j], "depthTex" , new RenderTargetIdentifier( m_TempDepthId ) );
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelEVSM_2[j], "outputTex", m_ShadowmapId );
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelEVSM_4[j], "depthTex" , new RenderTargetIdentifier( m_TempDepthId ) );
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelEVSM_4[j], "outputTex", m_ShadowmapId );
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelMSM[j] , "depthTex" , new RenderTargetIdentifier( m_TempDepthId ) );
cb.SetComputeTextureParam( m_MomentBlurCS, m_KernelMSM[j] , "outputTex", m_ShadowmapId );
}
while( i < cnt && m_EntryCache[i].current.slice == rendertargetSlice )
{
AdditionalLightData ald = lights[m_EntryCache[i].key.visibleIdx].light.GetComponent<AdditionalLightData>();
int shadowDataFormat;
int[] shadowData = ald.GetShadowData( out shadowDataFormat );
switch( m_EntryCache[i].current.shadowAlgo )
{
case GPUShadowAlgorithm.VSM:
{
kernelIdx = shadowData[2];
currentKernel = m_KernelVSM[kernelIdx];
}
break;
case GPUShadowAlgorithm.EVSM_2:
{
Debug.Assert( (GPUShadowAlgorithm) shadowDataFormat == GPUShadowAlgorithm.EVSM_2 );
float evsmExponent1 = ShadowUtils.Asfloat( shadowData[2] );
cb.SetComputeFloatParam( m_MomentBlurCS, "evsmExponent", evsmExponent1 );
kernelIdx = shadowData[4];
currentKernel = m_KernelEVSM_2[kernelIdx];
}
break;
case GPUShadowAlgorithm.EVSM_4:
{
Debug.Assert( (GPUShadowAlgorithm) shadowDataFormat == GPUShadowAlgorithm.EVSM_4 );
float evsmExponent1 = ShadowUtils.Asfloat( shadowData[2] );
float evsmExponent2 = ShadowUtils.Asfloat( shadowData[3] );
cb.SetComputeFloatParams( m_MomentBlurCS, "evsmExponents", new float[] { evsmExponent1, evsmExponent2 } );
kernelIdx = shadowData[4];
currentKernel = m_KernelEVSM_4[kernelIdx];
}
break;
case GPUShadowAlgorithm.MSM_Ham :
case GPUShadowAlgorithm.MSM_Haus:
{
kernelIdx = shadowData[3];
currentKernel = m_KernelMSM[kernelIdx];
}
break;
default: Debug.LogError( "Unknown shadow algorithm selected for momentum type shadow maps." ); break;
}
// TODO: Need a check here whether the shadowmap actually got updated, but right now that's queried on the cullResults.
Rect r = m_EntryCache[i].current.viewport;
cb.SetComputeIntParams( m_MomentBlurCS, "srcRect", new int[] { (int) r.x, (int) r.y, (int) r.width, (int) r.height } );
cb.SetComputeIntParams( m_MomentBlurCS, "dstRect", new int[] { (int) r.x, (int) r.y, (int) rendertargetSlice } );
cb.SetComputeFloatParams( m_MomentBlurCS, "blurWeightsStorage", m_BlurWeights[kernelIdx] );
cb.DispatchCompute( m_MomentBlurCS, currentKernel, ((int) r.width) / k_MomentBlurThreadsPerWorkgroup, (int) r.height / k_MomentBlurThreadsPerWorkgroup, 1 );
i++;
}
}
}
// -------------------------------------------------------------------------------------------------------------------------------------------------
//
// ShadowManager
//
// -------------------------------------------------------------------------------------------------------------------------------------------------

m_Shadowmaps = shadowmaps;
foreach( var sm in shadowmaps )
{
sm.Register( this );
sm.ReserveSlots( m_ShadowCtxt );
ShadowmapBase.ShadowSupport smsupport = sm.QueryShadowSupport();
for( int i = 0, bit = 1; i < (int) GPUShadowType.MAX; ++i, bit <<= 1 )

m_MaxShadows[(int)GPUShadowType.Point ,0] = m_MaxShadows[(int)GPUShadowType.Point ,1] = 4;
m_MaxShadows[(int)GPUShadowType.Spot ,0] = m_MaxShadows[(int)GPUShadowType.Spot ,1] = 8;
m_MaxShadows[(int)GPUShadowType.Directional,0] = m_MaxShadows[(int)GPUShadowType.Directional ,1] = 1;
m_MaxShadows[(int)GPUShadowType.Directional,0] = m_MaxShadows[(int)GPUShadowType.Directional ,1] = 2;
// and register itself
AdditionalLightDataEditor.SetRegistry( this );
}
public override void ProcessShadowRequests( FrameId frameId, CullResults cullResults, Camera camera, VisibleLight[] lights, ref uint shadowRequestsCount, int[] shadowRequests, out int[] shadowDataIndices )

switch( vl.lightType )
{
case LightType.Directional : add = m_MaxShadows[(int)GPUShadowType.Directional , 0]-- >= 0; shadowType = GPUShadowType.Directional; facecount = m_ShadowSettings.directionalLightCascadeCount; break;
case LightType.Point : add = m_MaxShadows[(int)GPUShadowType.Point , 0]-- >= 0; shadowType = GPUShadowType.Point ; facecount = 6; break;
case LightType.Spot : add = m_MaxShadows[(int)GPUShadowType.Spot , 0]-- >= 0; shadowType = GPUShadowType.Spot ; facecount = 1; break;
case LightType.Directional : add = --m_MaxShadows[(int)GPUShadowType.Directional , 0] >= 0; shadowType = GPUShadowType.Directional; facecount = m_ShadowSettings.directionalLightCascadeCount; break;
case LightType.Point : add = --m_MaxShadows[(int)GPUShadowType.Point , 0] >= 0; shadowType = GPUShadowType.Point ; facecount = 6; break;
case LightType.Spot : add = --m_MaxShadows[(int)GPUShadowType.Spot , 0] >= 0; shadowType = GPUShadowType.Spot ; facecount = 1; break;
}
if( add )

sreq.facemask = (uint) (1 << facecount) - 1;
sreq.shadowType = shadowType;
int sa, sv;
vl.light.GetComponent<AdditionalLightData>().GetShadowAlgorithm( out sa, out sv );
sreq.shadowAlgorithm = ShadowUtils.Pack( (ShadowAlgorithm) sa, (ShadowVariant) sv );
totalRequestCount += (uint) facecount;
requestsGranted.AddUnchecked( sreq );
totalSlots--;

// set light specific values that are not related to the shadowmap
GPUShadowType shadowtype;
ShadowUtils.MapLightType( ald.archetype, vl.lightType, out sd.lightType, out shadowtype );
sd.bias = l.shadowBias;
sd.quality = 0;
sd.bias = SystemInfo.usesReversedZBuffer ? l.shadowBias : -l.shadowBias;
sd.normalBias = l.shadowNormalBias;
shadowIndices.AddUnchecked( (int) shadowDatas.Count() );

52
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.hlsl


//
//#define SHADOWS_USE_SHADOWCTXT
#define SHADOWS_USE_SHADOWCTXT
#ifdef SHADOWS_USE_SHADOWCTXT
#define SHADOW_SUPPORTS_DYNAMIC_INDEXING 0 // only on >= sm 5.1

slice = (float)(shadowmapId & 0xffff);
}
void UnpackShadowType( uint packedShadowType, out uint shadowType, out uint shadowAlgorithm )
{
shadowType = packedShadowType >> 9;
shadowAlgorithm = packedShadowType & 0x1ff;
}
void UnpackShadowType( uint packedShadowType, out uint shadowType )
{
shadowType = packedShadowType >> 9;
}
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L );
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS );
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L );
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS );
// wedge in the actual shadow sampling algorithms

// default dispatchers for the individual shadow types (with and without screenspace support)
// point/spot light shadows
float GetPunctualShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
float GetPunctualShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L )
return EvalShadow_PunctualDepth(shadowContext, positionWS, shadowDataIndex, L);
return EvalShadow_PunctualDepth(shadowContext, positionWS, normalWS, shadowDataIndex, L);
float GetPunctualShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
float GetPunctualShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS )
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, normalWS, shadowDataIndex, L );
float GetDirectionalShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
float GetDirectionalShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L )
return EvalShadow_CascadedDepth( shadowContext, positionWS, shadowDataIndex, L );
return EvalShadow_CascadedDepth( shadowContext, positionWS, normalWS, shadowDataIndex, L );
float GetDirectionalShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
float GetDirectionalShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS )
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, normalWS, shadowDataIndex, L );
}
// include project specific shadow dispatcher. If this file is not empty, it MUST define which default shadows it's overriding

#ifndef SHADOW_DISPATCH_USE_CUSTOM_PUNCTUAL
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L )
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, normalWS, shadowDataIndex, L );
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS )
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L, unPositionSS );
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, normalWS, shadowDataIndex, L, unPositionSS );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L )
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, normalWS, shadowDataIndex, L );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS )
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L, unPositionSS );
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, normalWS, shadowDataIndex, L, unPositionSS );
}
#endif

202
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithms.hlsl


// There are two variants provided, one takes the texture and sampler explicitly so they can be statically passed in.
// The variant without resource parameters dynamically accesses the texture when sampling.
// Helper function to offset depth based on the surface normal and light direction.
// If the light hits the surface perpendicularly there will be no offset.
float3 EvalShadow_NormalBias( float3 normalWS, float NoL, float2 texelSize, float normalBias )
{
return 2.0 * max( texelSize.x, texelSize.y ) * normalBias * (1.0 - NoL) * normalWS;
}
// apply a bias
posCS.z -= sd.bias;
float3 posNDC = posCS.xyz / posCS.w;
// calc TCs
float3 posTC = posNDC * 0.5 + 0.5;

//
// Point shadows
//
float EvalShadow_PointDepth( ShadowContext shadowContext, float3 positionWS, int index, float3 L )
float EvalShadow_PointDepth( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int index, float3 L )
ShadowData sd = shadowContext.shadowDatas[index + faceIndex];
ShadowData sd = shadowContext.shadowDatas[index + 1 + faceIndex];
uint payloadOffset = GetPayloadOffset( sd );
// sample the texture
// get the algorithm
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
// sample the texture according to the given algorithm
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, texIdx, sampIdx );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
float EvalShadow_PointDepth( ShadowContext shadowContext, Texture2DArray tex, SamplerComparisonState compSamp, float3 positionWS, int index, float3 L )
{
// load the right shadow data for the current face
int faceIndex = 0;
GetCubeFaceID( L, faceIndex );
ShadowData sd = shadowContext.shadowDatas[index + faceIndex];
// get shadowmap texcoords
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
// sample the texture
float slice;
UnpackShadowmapId( sd.id, slice );
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, tex, compSamp );
}
#define EvalShadow_PointDepth_( _samplerType ) \
float EvalShadow_PointDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, float3 positionWS, float3 normalWS, int index, float3 L ) \
{ \
/* load the right shadow data for the current face */ \
int faceIndex = 0; \
GetCubeFaceID( L, faceIndex ); \
ShadowData sd = shadowContext.shadowDatas[index + 1 + faceIndex]; \
uint payloadOffset = GetPayloadOffset( sd ); \
/* get shadowmap texcoords */ \
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
/* sample the texture */ \
float slice; \
UnpackShadowmapId( sd.id, slice ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, tex, samp ); \
}
EvalShadow_PointDepth_( SamplerComparisonState )
EvalShadow_PointDepth_( SamplerState )
#undef EvalShadow_PointDepth_
float EvalShadow_SpotDepth( ShadowContext shadowContext, float3 positionWS, int index, float3 L )
float EvalShadow_SpotDepth( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int index, float3 L )
uint payloadOffset = GetPayloadOffset( sd );
// sample the texture
// get the algorithm
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
// sample the texture according to the given algorithm
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, texIdx, sampIdx );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
float EvalShadow_SpotDepth( ShadowContext shadowContext, Texture2DArray tex, SamplerComparisonState compSamp, float3 positionWS, int index, float3 L )
{
// load the right shadow data for the current face
ShadowData sd = shadowContext.shadowDatas[index];
// get shadowmap texcoords
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
// sample the texture
float slice;
UnpackShadowmapId( sd.id, slice );
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, tex, compSamp );
}
#define EvalShadow_SpotDepth_( _samplerType ) \
float EvalShadow_SpotDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, float3 positionWS, float3 normalWS, int index, float3 L ) \
{ \
/* load the right shadow data for the current face */ \
ShadowData sd = shadowContext.shadowDatas[index]; \
uint payloadOffset = GetPayloadOffset( sd ); \
/* get shadowmap texcoords */ \
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
/* sample the texture */ \
float slice; \
UnpackShadowmapId( sd.id, slice ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, tex, samp ); \
}
EvalShadow_SpotDepth_( SamplerComparisonState )
EvalShadow_SpotDepth_( SamplerState )
#undef EvalShadow_SpotDepth_
float EvalShadow_PunctualDepth( ShadowContext shadowContext, float3 positionWS, int index, float3 L )
float EvalShadow_PunctualDepth( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int index, float3 L )
// get the algorithm
uint shadowType, shadowAlgorithm;
UnpackShadowType( shadowContext.shadowDatas[index].shadowType, shadowType );
if( shadowContext.shadowDatas[index].shadowType == GPUSHADOWTYPE_POINT )
if( shadowType == GPUSHADOWTYPE_POINT )
{
faceIndex++;
}
uint payloadOffset = GetPayloadOffset( sd );
// sample the texture
// sample the texture according to the given algorithm
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, texIdx, sampIdx );
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
float EvalShadow_PunctualDepth( ShadowContext shadowContext, Texture2DArray tex, SamplerComparisonState compSamp, float3 positionWS, int index, float3 L )
{
// load the right shadow data for the current face
int faceIndex = 0;
[branch]
if( shadowContext.shadowDatas[index].shadowType == GPUSHADOWTYPE_POINT )
GetCubeFaceID( L, faceIndex );
ShadowData sd = shadowContext.shadowDatas[index + faceIndex];
// get shadowmap texcoords
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
// sample the texture
float slice;
UnpackShadowmapId( sd.id, slice );
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, tex, compSamp );
}
#define EvalShadow_PunctualDepth_( _samplerType ) \
float EvalShadow_PunctualDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, float3 positionWS, float3 normalWS, int index, float3 L ) \
{ \
/* load the right shadow data for the current face */ \
int faceIndex = 0; \
/* get the shadow type */ \
uint shadowType; \
UnpackShadowType( shadowContext.shadowDatas[index].shadowType, shadowType ); \
\
[branch] \
if( shadowType == GPUSHADOWTYPE_POINT ) \
{ \
GetCubeFaceID( L, faceIndex ); \
faceIndex++; \
} \
\
ShadowData sd = shadowContext.shadowDatas[index + faceIndex]; \
uint payloadOffset = GetPayloadOffset( sd ); \
/* get shadowmap texcoords */ \
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
/* sample the texture */ \
float slice; \
UnpackShadowmapId( sd.id, slice ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, tex, samp ); \
}
EvalShadow_PunctualDepth_( SamplerComparisonState )
EvalShadow_PunctualDepth_( SamplerState )
#undef EvalShadow_PunctualDepth_
//
// Directional shadows (cascaded shadow map)

return uint( 4.0 - dot( weights, float4(4.0, 3.0, 2.0, 1.0 ) ) );
}
void EvalShadow_LoadSplitSpheres( ShadowContext shadowContext, int index, out float4 splitSpheres[4] )
uint EvalShadow_LoadSplitSpheres( ShadowContext shadowContext, int index, out float4 splitSpheres[4] )
{
uint offset = GetPayloadOffset( shadowContext.shadowDatas[index] );

splitSpheres[3] = asfloat( shadowContext.payloads[offset + 3] );
return offset + 4;
float EvalShadow_CascadedDepth( ShadowContext shadowContext, float3 positionWS, int index, float3 L )
float EvalShadow_CascadedDepth( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int index, float3 L )
EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres );
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres );
ShadowData sd = shadowContext.shadowDatas[index + shadowSplitIndex];
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex];
// normal based bias
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), sd.texelSizeRcp, sd.normalBias );
// get shadowmap texcoords
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS );

UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
return SampleShadow_PCF_9tap_Adaptive( shadowContext, sd.texelSizeRcp, posTC, slice, texIdx, sampIdx );
}
float EvalShadow_CascadedDepth( ShadowContext shadowContext, Texture2DArray tex, SamplerComparisonState compSamp, float3 positionWS, int index, float3 L )
{
// load the right shadow data for the current face
float4 dirShadowSplitSpheres[4];
EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres );
uint shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres );
ShadowData sd = shadowContext.shadowDatas[index + shadowSplitIndex];
// get shadowmap texcoords
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
// sample the texture
float slice;
UnpackShadowmapId(sd.id, slice);
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
return SampleShadow_PCF_9tap_Adaptive( shadowContext, sd.texelSizeRcp, posTC, slice, tex, compSamp );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
#define EvalShadow_CascadedDepth_( _samplerType ) \
float EvalShadow_CascadedDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, float3 positionWS, float3 normalWS, int index, float3 L ) \
{ \
/* load the right shadow data for the current face */ \
float4 dirShadowSplitSpheres[4]; \
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres ); \
uint shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres ); \
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex]; \
/* normal based bias */ \
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), sd.texelSizeRcp, sd.normalBias ); \
/* get shadowmap texcoords */ \
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
/* sample the texture */ \
float slice; \
UnpackShadowmapId( sd.id, slice ); \
\
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, tex, samp ); \
}
EvalShadow_CascadedDepth_( SamplerComparisonState )
EvalShadow_CascadedDepth_( SamplerState )
#undef EvalShadow_CascadedDepth_

30
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithmsCustom.hlsl


// This file is empty on purpose. Projects can put their custom shadow algorithms in here so they get automatically included by Shadow.hlsl.
float EvalShadow_CascadedMomentum( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
float EvalShadow_CascadedMomentum( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L )
return 1.0;
// load the right shadow data for the current face
float4 dirShadowSplitSpheres[4];
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, shadowDataIndex, dirShadowSplitSpheres );
uint shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres );
ShadowData sd = shadowContext.shadowDatas[shadowDataIndex + 1 + shadowSplitIndex];
// normal based bias
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), sd.texelSizeRcp, sd.normalBias );
// get shadowmap texcoords
float3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
// sample the texture
uint texIdx, sampIdx;
float slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
switch( shadowAlgorithm )
{
case (GPUSHADOWALGORITHM_CUSTOM + 1): return 1.0;
default: return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
}
float EvalShadow_CascadedMomentum( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
float EvalShadow_CascadedMomentum( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS )
return 1.0;
return EvalShadow_CascadedMomentum( shadowContext, positionWS, normalWS, shadowDataIndex, L );
}

318
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs


using UnityEngine.Rendering;
using System;
using System.Collections.Generic;
namespace UnityEngine.Experimental.Rendering.HDPipeline
{

All = Point | Spot | Directional
};
public enum ShadowAlgorithm // 6 bits
{
PCF,
VSM,
EVSM,
MSM,
Custom = 32
};
public enum ShadowVariant // 3 bits
{
V0,
V1,
V2,
V3,
V4,
V5,
V6,
V7
}
public enum GPUShadowSampling
public enum GPUShadowAlgorithm // 9 bits
PCF_1tap,
PCF_9Taps_Adaptive,
VSM_1tap,
MSM_1tap
};
PCF_1tap = ShadowAlgorithm.PCF << 3 | ShadowVariant.V0,
PCF_9tap = ShadowAlgorithm.PCF << 3 | ShadowVariant.V1,
VSM = ShadowAlgorithm.VSM << 3,
EVSM_2 = ShadowAlgorithm.EVSM << 3 | ShadowVariant.V0,
EVSM_4 = ShadowAlgorithm.EVSM << 3 | ShadowVariant.V1,
MSM_Ham = ShadowAlgorithm.MSM << 3 | ShadowVariant.V0,
MSM_Haus = ShadowAlgorithm.MSM << 3 | ShadowVariant.V1,
Custom = ShadowAlgorithm.Custom << 3
}
// Central location for storing various shadow constants and bitmasks. These can be used to pack enums into ints, for example.
// These are all guaranteed to be positive, but C# doesn't like uints, so they're all ints.
public static class ShadowConstants
{
public struct Counts
{
public const int k_ShadowAlgorithm = 64;
public const int k_ShadowVariant = 8;
public const int k_GPUShadowType = 3;
}
public struct Bits
{
public const int k_ShadowAlgorithm = 6;
public const int k_ShadowVariant = 3;
public const int k_GPUShadowAlgorithm = k_ShadowAlgorithm + k_ShadowVariant;
public const int k_GPUShadowType = 4;
}
public struct Masks
{
public const int k_ShadowAlgorithm = (1 << Bits.k_ShadowAlgorithm ) - 1;
public const int k_ShadowVariant = (1 << Bits.k_ShadowVariant ) - 1;
public const int k_GPUShadowAlgorithm = (1 << Bits.k_GPUShadowAlgorithm ) - 1;
public const int k_GPUShadowType = (1 << Bits.k_GPUShadowType ) - 1;
}
}
// Shadow Registry for exposing shadow features to the UI
public class ShadowRegistry
{
public delegate void VariantDelegate( Light l, ShadowAlgorithm dataAlgorithm, ShadowVariant dataVariant, ref int[] dataContainer );
struct Entry
{
public string algorithmDesc;
public int variantsAvailable;
public string[] variantDescs;
public VariantDelegate[] variantDels;
}
Dictionary<ShadowAlgorithm, Entry>[] m_Entries = new Dictionary<ShadowAlgorithm, Entry>[ShadowConstants.Counts.k_GPUShadowType]
{ new Dictionary<ShadowAlgorithm, Entry>(),
new Dictionary<ShadowAlgorithm, Entry>(),
new Dictionary<ShadowAlgorithm, Entry>() };
public void ClearRegistry()
{
foreach( var d in m_Entries )
d.Clear();
}
public void Register( GPUShadowType type, ShadowAlgorithm algorithm, string algorithmDescriptor, ShadowVariant[] variants, string[] variantDescriptors, VariantDelegate[] variantDelegates )
{
if( Validate( algorithmDescriptor, variants, variantDescriptors, variantDelegates ) )
Register( m_Entries[(int)type], algorithm, algorithmDescriptor, variants, variantDescriptors, variantDelegates );
}
private bool Validate( string algorithmDescriptor, ShadowVariant[] variants, string[] variantDescriptors, VariantDelegate[] variantDelegates )
{
if( string.IsNullOrEmpty( algorithmDescriptor ) )
{
Debug.LogError( "Tried to register a shadow algorithm but the algorithm descriptor is empty." );
return false;
}
if( variantDescriptors == null || variantDescriptors.Length == 0 )
{
Debug.LogError( "Tried to register a shadow algorithm (" + algorithmDescriptor + ") but the variant descriptors are empty. At least one variant descriptor is required for registration." );
return false;
}
if( variantDescriptors.Length > ShadowConstants.Counts.k_ShadowVariant )
{
Debug.LogError( "Tried to register a shadow algorithm (" + algorithmDescriptor + ") with more than the valid amount of variants. Variant count: " + variantDescriptors.Length + ", valid count: " + ShadowConstants.Counts.k_ShadowVariant + "." );
return false;
}
if( variantDescriptors.Length != variants.Length )
{
Debug.LogError( "Tried to register a shadow algorithm (" + algorithmDescriptor + ") but the length of variant descriptors (" + variantDescriptors.Length + ") does not match the length of variants (" + variants.Length + ")." );
return false;
}
if( variantDelegates.Length != variants.Length )
{
Debug.LogError( "Tried to register a shadow algorithm (" + algorithmDescriptor + ") but the length of variant delegates (" + variantDelegates.Length + ") does not match the length of variants (" + variants.Length + ")." );
return false;
}
return true;
}
private void Register( Dictionary<ShadowAlgorithm, Entry> dict, ShadowAlgorithm algorithm, string algorithmDescriptor, ShadowVariant[] variants, string[] variantDescriptors, VariantDelegate[] variantDelegates )
{
if( !dict.ContainsKey( algorithm ) )
{
Entry e;
e.algorithmDesc = algorithmDescriptor;
e.variantsAvailable = variants.Length;
e.variantDescs = new string[ShadowConstants.Counts.k_ShadowVariant];
e.variantDels = new VariantDelegate[ShadowConstants.Counts.k_ShadowVariant];
for( uint i = 0, cnt = (uint) variants.Length; i < cnt; ++i )
{
e.variantDescs[(uint) variants[i]] = variantDescriptors[i];
e.variantDels[(uint) variants[i]] = variantDelegates[i];
}
dict.Add( algorithm, e );
}
else
{
var entry = dict[algorithm];
for( uint i = 0, cnt = (uint) variants.Length; i < cnt; ++i )
{
if( string.IsNullOrEmpty( entry.variantDescs[(uint) variants[i]] ) )
{
entry.variantsAvailable++;
entry.variantDescs[(uint) variants[i]] = variantDescriptors[i];
entry.variantDels[(uint) variants[i]] = variantDelegates[i];
}
else
Debug.Log( "Tried to register variant " + variants[i] + " for algorithm " + algorithm + ", but this variant is already registered. Skipping registration." );
}
}
}
public void Draw( Light l )
{
AdditionalLightData ald = l.GetComponent<AdditionalLightData>();
Debug.Assert(ald != null, "Light has no valid AdditionalLightData component attached.");
GPULightType lightType;
GPUShadowType shadowType;
ShadowUtils.MapLightType( ald.archetype, l.type, out lightType, out shadowType );
// check if this has supported shadows
if( (int) shadowType >= ShadowConstants.Counts.k_GPUShadowType )
return;
int shadowAlgorithm;
int shadowVariant;
ald.GetShadowAlgorithm( out shadowAlgorithm, out shadowVariant );
DrawWidgets( l, shadowType, (ShadowAlgorithm) shadowAlgorithm, (ShadowVariant) shadowVariant );
}
void DrawWidgets( Light l, GPUShadowType shadowType, ShadowAlgorithm shadowAlgorithm, ShadowVariant shadowVariant )
{
var dict = m_Entries[(int)shadowType];
int[] algoOptions = new int[dict.Count];
GUIContent[] algoDescs = new GUIContent[dict.Count];
int idx = 0;
foreach( var entry in dict )
{
algoOptions[idx] = (int) entry.Key;
algoDescs[idx] = new GUIContent( entry.Value.algorithmDesc );
idx++;
}
UnityEditor.EditorGUI.BeginChangeCheck();
shadowAlgorithm = (ShadowAlgorithm) UnityEditor.EditorGUILayout.IntPopup( new GUIContent( "Shadow Algorithm" ), (int) shadowAlgorithm, algoDescs, algoOptions );
if( UnityEditor.EditorGUI.EndChangeCheck() )
shadowVariant = 0;
UnityEditor.EditorGUI.indentLevel++;
Entry e = dict[shadowAlgorithm];
int varsAvailable = e.variantsAvailable;
int[] varOptions = new int[varsAvailable];
GUIContent[] varDescs = new GUIContent[varsAvailable];
idx = 0;
for( int writeIdx = 0; writeIdx < varsAvailable; idx++ )
{
if( e.variantDels[idx] != null )
{
varOptions[writeIdx] = idx;
varDescs[writeIdx] = new GUIContent( e.variantDescs[idx] );
writeIdx++;
}
}
UnityEditor.EditorGUILayout.BeginHorizontal();
shadowVariant = (ShadowVariant) UnityEditor.EditorGUILayout.IntPopup( new GUIContent( "Variant" ), (int) shadowVariant, varDescs, varOptions );
AdditionalLightData ald = l.GetComponent<AdditionalLightData>();
GPUShadowAlgorithm packedAlgo = ShadowUtils.Pack( shadowAlgorithm, shadowVariant );
int[] shadowData = null;
if( !GUILayout.Button( "Reset", GUILayout.MaxWidth( 80.0f ) ) )
shadowData = ald.GetShadowData( (int) packedAlgo );
UnityEditor.EditorGUILayout.EndHorizontal();
e.variantDels[(int) shadowVariant]( l, shadowAlgorithm, shadowVariant, ref shadowData );
ald.SetShadowAlgorithm( (int) shadowAlgorithm, (int) shadowVariant, (int) packedAlgo, shadowData );
UnityEditor.EditorGUI.indentLevel--;
}
}
// This is the struct passed into shaders
[GenerateHLSL]

public Vector4 scaleOffset; // scale and offset of shadowmap in atlas
public Vector2 texelSizeRcp; // reciprocal of the shadowmap's texel size in x and y
public uint id; // packed texture id, sampler id and slice idx
public GPUShadowType shadowType; // determines the shadow algorithm, i.e. which map to sample and how to interpret the data
public uint shadowType; // determines the shadow algorithm, i.e. which map to sample and how to interpret the data
public float quality; // some quality parameters
public float normalBias; // bias based on the normal
public void PackShadowmapId( uint texIdx, uint sampIdx, uint slice )
{

id = texIdx << 24 | sampIdx << 16 | slice;
}
public void PackShadowType( GPUShadowType type, GPUShadowAlgorithm algorithm )
{
shadowType = (uint)type << 9 | (uint) algorithm;
}
};
public struct FrameId

// -------------- Begin temporary structs that need to be replaced at some point ---------------
public struct SamplerState
{
FilterMode filterMode;
TextureWrapMode wrapMode;
uint anisotropy;
public static SamplerState Default()
{
SamplerState defaultState;
defaultState.filterMode = FilterMode.Bilinear;
defaultState.wrapMode = TextureWrapMode.Clamp;
defaultState.anisotropy = 1;
return defaultState;
}
public static bool operator ==( SamplerState lhs, SamplerState rhs ) { return false; }
public static bool operator !=( SamplerState lhs, SamplerState rhs ) { return true; }
public static bool operator ==( SamplerState lhs, SamplerState rhs )
{
return false; // TODO: Remove this once shared samplers are in
//return lhs.filterMode == rhs.filterMode && lhs.wrapMode == rhs.wrapMode && lhs.anisotropy == rhs.anisotropy;
}
public static bool operator !=( SamplerState lhs, SamplerState rhs ) { return !(lhs == rhs); }
}
}
FilterMode filterMode;
TextureWrapMode wrapMode;
uint anisotropy;
public static ComparisonSamplerState Default()
{
ComparisonSamplerState defaultState;
defaultState.filterMode = FilterMode.Bilinear;
defaultState.wrapMode = TextureWrapMode.Clamp;
defaultState.anisotropy = 1;
return defaultState;
}
public static bool operator ==(ComparisonSamplerState lhs, ComparisonSamplerState rhs) { return false; }
public static bool operator !=(ComparisonSamplerState lhs, ComparisonSamplerState rhs) { return true; }
public static bool operator ==(ComparisonSamplerState lhs, ComparisonSamplerState rhs)
{
return false;
//return lhs.filterMode == rhs.filterMode && lhs.wrapMode == rhs.wrapMode && lhs.anisotropy == rhs.anisotropy;
}
public static bool operator !=(ComparisonSamplerState lhs, ComparisonSamplerState rhs) { return !(lhs == rhs); }
// Helper struct for passing arbitrary data into shaders. Entry size is 4 ints to minimize load instructions
// in shaders. This should really be int p[4] but C# doesn't let us do that.
public struct ShadowPayload
{
public int p0;

p2 = ShadowUtils.Asint( v2 );
p3 = ShadowUtils.Asint( v3 );
}
public void Set( int v0, int v1, int v2, int v3 )
{
p0 = v0;
p1 = v1;
p2 = v2;
p3 = v3;
}
public void Set( Vector4 v ) { Set( v.x, v.y, v.z, v.w ); }
}

Spot = 1 << GPUShadowType.Spot,
Directional = 1 << GPUShadowType.Directional
}
public struct ShadowRequest
{
private const byte k_IndexBits = 24;

// combined face mask and visible light index
private uint m_MaskIndex;
private int m_ShadowTypeAndAlgorithm;
public GPUShadowType shadowType { get; set; }
public GPUShadowType shadowType
{
get { return (GPUShadowType) (m_ShadowTypeAndAlgorithm >> ShadowConstants.Bits.k_GPUShadowAlgorithm); }
set { m_ShadowTypeAndAlgorithm = ((int)value << ShadowConstants.Bits.k_GPUShadowAlgorithm) | (m_ShadowTypeAndAlgorithm & ShadowConstants.Masks.k_GPUShadowAlgorithm); }
}
public GPUShadowAlgorithm shadowAlgorithm
{
get { return (GPUShadowAlgorithm) (m_ShadowTypeAndAlgorithm & ShadowConstants.Masks.k_GPUShadowAlgorithm); }
set { m_ShadowTypeAndAlgorithm = (m_ShadowTypeAndAlgorithm & ~(ShadowConstants.Masks.k_GPUShadowAlgorithm)) | (int)value; }
}
// index into the visible lights array
public uint index
{

return m_ShadowmapFormat == RenderTextureFormat.Shadowmap || m_ShadowmapFormat == RenderTextureFormat.Depth;
}
public void Register( ShadowRegistry registry )
{
int bit = 1;
for( GPUShadowType i = GPUShadowType.Point; i < GPUShadowType.MAX; ++i, bit <<= 1 )
{
if( ((int)m_ShadowSupport & bit) != 0 )
Register( i, registry );
}
}
public ShadowSupport QueryShadowSupport() { return m_ShadowSupport; }
public uint GetMaxPayload() { return m_MaxPayloadCount; }

abstract public void Update( FrameId frameId, ScriptableRenderContext renderContext, CullResults cullResults, VisibleLight[] lights );
abstract public void ReserveSlots( ShadowContextStorage sc );
abstract public void Fill( ShadowContextStorage cs );
abstract protected void Register( GPUShadowType type, ShadowRegistry registry );
}
interface IShadowManager

void BindResources( ScriptableRenderContext renderContext );
}
abstract public class ShadowManagerBase : IShadowManager
abstract public class ShadowManagerBase : ShadowRegistry, IShadowManager
{
public abstract void ProcessShadowRequests( FrameId frameId, CullResults cullResults, Camera camera, VisibleLight[] lights, ref uint shadowRequestsCount, int[] shadowRequests, out int[] shadowDataIndices );
public abstract void RenderShadows( FrameId frameId, ScriptableRenderContext renderContext, CullResults cullResults, VisibleLight[] lights );

26
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs.hlsl


//
// This file was automatically generated from Assets/ScriptableRenderLoop/HDRenderPipeline/Shadow/ShadowBase.cs. Please don't edit by hand.
// This file was automatically generated from Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs. Please don't edit by hand.
//
#ifndef SHADOWBASE_CS_HLSL

#define GPUSHADOWTYPE_SPOT (1)
#define GPUSHADOWTYPE_DIRECTIONAL (2)
#define GPUSHADOWTYPE_MAX (3)
#define GPUSHADOWTYPE_UNKNOWN (3)
#define GPUSHADOWTYPE_ALL (3)
//
// UnityEngine.Experimental.Rendering.HDPipeline.GPUShadowAlgorithm: static fields
//
#define GPUSHADOWALGORITHM_PCF_1TAP (0)
#define GPUSHADOWALGORITHM_PCF_9TAP (1)
#define GPUSHADOWALGORITHM_VSM (8)
#define GPUSHADOWALGORITHM_EVSM_2 (16)
#define GPUSHADOWALGORITHM_EVSM_4 (17)
#define GPUSHADOWALGORITHM_MSM_HAM (24)
#define GPUSHADOWALGORITHM_MSM_HAUS (25)
#define GPUSHADOWALGORITHM_CUSTOM (256)
// Generated from UnityEngine.Experimental.Rendering.HDPipeline.ShadowExp.ShadowData
// PackingRules = Exact

float4 scaleOffset;
float2 texelSizeRcp;
uint id;
int shadowType;
uint shadowType;
float quality;
float normalBias;
};
//

{
return value.id;
}
int GetShadowType(ShadowData value)
uint GetShadowType(ShadowData value)
{
return value.shadowType;
}

{
return value.bias;
}
float GetQuality(ShadowData value)
float GetNormalBias(ShadowData value)
return value.quality;
return value.normalBias;
}

17
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowContext.hlsl


// This can be custom for each project and needs to be in sync with the ShadowMgr
#define SHADOWCONTEXT_MAX_TEX2DARRAY 2
#define SHADOWCONTEXT_MAX_TEX2DARRAY 3
#define SHADOWCONTEXT_MAX_TEXCUBEARRAY 1
#define SHADOWCONTEXT_MAX_SAMPLER 1
#define SHADOWCONTEXT_MAX_COMPSAMPLER 2

StructuredBuffer<ShadowData> _ShadowDatasExp;
StructuredBuffer<int4> _ShadowPayloads;
TEXTURE2D_ARRAY(_ShadowmapExp_Dir_VSM);
SAMPLER2D(sampler_ShadowmapExp_Dir_VSM);
StructuredBuffer<ShadowData> _ShadowDatasExp;
StructuredBuffer<int4> _ShadowPayloads;
sc.tex2DArray[0] = _ShadowmapExp_Dir;
sc.tex2DArray[1] = _ShadowmapExp_PointSpot;
sc.tex2DArray[0] = _ShadowmapExp_Dir_VSM;
sc.tex2DArray[1] = _ShadowmapExp_Dir;
sc.tex2DArray[2] = _ShadowmapExp_PointSpot;
sc.samplers[0] = sampler_ShadowmapExp_Dir_VSM;
sc.compSamplers[0] = sampler_ShadowmapExp_Dir;
sc.compSamplers[1] = sampler_ShadowmapExp_PointSpot;
return sc;

49
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowDispatch.hlsl


// example of overriding directional lights
//#define SHADOW_DISPATCH_USE_CUSTOM_DIRECTIONAL
#ifdef SHADOW_DISPATCH_USE_CUSTOM_DIRECTIONAL
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L )
SamplerComparisonState compSamp = shadowContext.compSamplers[0];
SamplerState samp = shadowContext.samplers[0];
uint algo = GPUSHADOWALGORITHM_MSM_HAUS;
return EvalShadow_CascadedDepth( shadowContext, tex, compSamp, positionWS, shadowDataIndex, L );
//return EvalShadow_CascadedMomentum( shadowContext, positionWS, shadowDataIndex, L );
return EvalShadow_CascadedDepth( shadowContext, algo, tex, samp, positionWS, normalWS, shadowDataIndex, L );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS )
Texture2DArray tex = shadowContext.tex2DArray[0];
SamplerComparisonState compSamp = shadowContext.compSamplers[0];
return EvalShadow_CascadedDepth( shadowContext, tex, compSamp, positionWS, shadowDataIndex, L );
//return EvalShadow_CascadedMomentum( shadowContext, positionWS, shadowDataIndex, L );
return GetDirectionalShadowAttenuation( shadowContext, positionWS, normalWS, shadowDataIndex, L );
}
#endif

// and that on the C# side the shadowContext bindDelegate binds the correct resource to the correct texture id.
//#define SHADOW_DISPATCH_USE_CUSTOM_PUNCTUAL
#ifdef SHADOW_DISPATCH_USE_CUSTOM_PUNCTUAL
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
#define SHADOW_USE_SEPARATE_ALGOS 0
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L )
Texture2DArray tex = shadowContext.tex2DArray[1];
Texture2DArray tex = shadowContext.tex2DArray[2];
return EvalShadow_PunctualDepth( shadowContext, tex, compSamp, positionWS, shadowDataIndex, L );
#if SHADOW_USE_SEPARATE_ALGOS != 0
// example for choosing different algos for point and spot lights
ShadowData sd = shadowContext.shadowDatas[shadowDataIndex];
uint shadowType;
UnpackShadowType( sd.shadowType, shadowType );
[branch]
if( shadowType == GPUSHADOWTYPE_POINT )
return EvalShadow_PointDepth( shadowContext, GPUSHADOWALGORITHM_PCF_1TAP, tex, compSamp, positionWS, normalWS, shadowDataIndex, L );
else
return EvalShadow_SpotDepth( shadowContext, GPUSHADOWALGORITHM_PCF_9TAP, tex, compSamp, positionWS, normalWS, shadowDataIndex, L );
#else
// example for choosing the same algo
uint algo = GPUSHADOWALGORITHM_PCF_1TAP;
return EvalShadow_PunctualDepth( shadowContext, algo, tex, compSamp, positionWS, normalWS, shadowDataIndex, L );
#endif
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float2 unPositionSS )
Texture2DArray tex = shadowContext.tex2DArray[1];
SamplerComparisonState compSamp = shadowContext.compSamplers[1];
return EvalShadow_PunctualDepth( shadowContext, tex, compSamp, positionWS, shadowDataIndex, L );
return GetPunctualShadowAttenuation( shadowContext, positionWS, normalWS, shadowDataIndex, L );
#undef SHADOW_USE_SEPARATE_ALGOS
#endif

221
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowSampling.hlsl


//
// 1 tap PCF sampling
//
float SampleShadow_PCF_1tap( ShadowContext shadowContext, float3 tcs, uint slice, uint texIdx, uint sampIdx )
float SampleShadow_PCF_1tap( ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, float bias, uint slice, uint texIdx, uint sampIdx )
float depthBias = asfloat( shadowContext.payloads[payloadOffset].x );
payloadOffset++;
// add the depth bias
tcs.z += depthBias;
float SampleShadow_PCF_1tap( ShadowContext shadowContext, float3 tcs, uint slice, Texture2DArray tex, SamplerComparisonState compSamp )
float SampleShadow_PCF_1tap( ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, float bias, uint slice, Texture2DArray tex, SamplerComparisonState compSamp )
float depthBias = asfloat( shadowContext.payloads[payloadOffset].x );
payloadOffset++;
// add the depth bias
tcs.z += depthBias;
// sample the texture
return SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, tcs, slice );
}

//
float SampleShadow_PCF_9tap_Adaptive( ShadowContext shadowContext, float2 texelSizeRcp, float3 tcs, uint slice, uint texIdx, uint sampIdx )
float SampleShadow_PCF_9tap_Adaptive( ShadowContext shadowContext, inout uint payloadOffset, float2 texelSizeRcp, float3 tcs, float bias, uint slice, uint texIdx, uint sampIdx )
float2 params = asfloat( shadowContext.payloads[payloadOffset].xy );
float depthBias = params.x;
float filterSize = params.y;
payloadOffset++;
texelSizeRcp *= filterSize;
// add the depth bias
tcs.z += depthBias;
// Terms0 are weights for the individual samples, the other terms are offsets in texel space
float4 vShadow3x3PCFTerms0 = float4( 20.0f / 267.0f, 33.0f / 267.0f, 55.0f / 267.0f, 0.0f );
float4 vShadow3x3PCFTerms1 = float4( texelSizeRcp.x, texelSizeRcp.y, -texelSizeRcp.x, -texelSizeRcp.y );

return flSum;
}
float SampleShadow_PCF_9tap_Adaptive(ShadowContext shadowContext, float2 texelSizeRcp, float3 tcs, uint slice, Texture2DArray tex, SamplerComparisonState compSamp )
float SampleShadow_PCF_9tap_Adaptive(ShadowContext shadowContext, inout uint payloadOffset, float2 texelSizeRcp, float3 tcs, float bias, uint slice, Texture2DArray tex, SamplerComparisonState compSamp )
float2 params = asfloat( shadowContext.payloads[payloadOffset].xy );
float depthBias = params.x;
float filterSize = params.y;
payloadOffset++;
texelSizeRcp *= filterSize;
// add the depth bias
tcs.z += depthBias;
// Terms0 are weights for the individual samples, the other terms are offsets in texel space
float4 vShadow3x3PCFTerms0 = float4(20.0f / 267.0f, 33.0f / 267.0f, 55.0f / 267.0f, 0.0f);
float4 vShadow3x3PCFTerms1 = float4( texelSizeRcp.x, texelSizeRcp.y, -texelSizeRcp.x, -texelSizeRcp.y);

return flSum;
}
#include "ShadowMoments.hlsl"
//
// 1 tap VSM sampling
//
float SampleShadow_VSM_1tap( ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, uint slice, uint texIdx, uint sampIdx )
{
float depth = tcs.z;
float2 params = asfloat( shadowContext.payloads[payloadOffset].xy );
float lightLeakBias = params.x;
float varianceBias = params.y;
payloadOffset++;
float2 moments = SampleShadow_T2DA( shadowContext, texIdx, sampIdx, tcs.xy, slice ).xy;
return ShadowMoments_ChebyshevsInequality( moments, depth, varianceBias, lightLeakBias );
}
float SampleShadow_VSM_1tap(ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, uint slice, Texture2DArray tex, SamplerState samp )
{
float depth = tcs.z;
float2 params = asfloat( shadowContext.payloads[payloadOffset].xy );
float lightLeakBias = params.x;
float varianceBias = params.y;
payloadOffset++;
float2 moments = SAMPLE_TEXTURE2D_ARRAY_LOD( tex, samp, tcs.xy, slice, 0.0 ).xy;
return ShadowMoments_ChebyshevsInequality( moments, depth, varianceBias, lightLeakBias );
}
//
// 1 tap EVSM sampling
//
float SampleShadow_EVSM_1tap( ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, uint slice, uint texIdx, uint sampIdx, bool fourMoments )
{
float depth = tcs.z;
float4 params = asfloat( shadowContext.payloads[payloadOffset] );
float lightLeakBias = params.x;
float varianceBias = params.y;
float2 evsmExponents = params.zw;
payloadOffset++;
float2 warpedDepth = ShadowMoments_WarpDepth( depth, evsmExponents );
float4 moments = SampleShadow_T2DA( shadowContext, texIdx, sampIdx, tcs.xy, slice );
// Derivate of warping at depth
float2 depthScale = evsmExponents * warpedDepth;
float2 minVariance = depthScale * depthScale * varianceBias;
[branch]
if( fourMoments )
{
float posContrib = ShadowMoments_ChebyshevsInequality( moments.xz, warpedDepth.x, minVariance.x, lightLeakBias );
float negContrib = ShadowMoments_ChebyshevsInequality( moments.yw, warpedDepth.y, minVariance.y, lightLeakBias );
return min( posContrib, negContrib );
}
else
{
return ShadowMoments_ChebyshevsInequality( moments.xy, warpedDepth.x, minVariance.x, lightLeakBias );
}
}
float SampleShadow_EVSM_1tap( ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, uint slice, Texture2DArray tex, SamplerState samp, bool fourMoments )
{
float depth = tcs.z;
float4 params = asfloat( shadowContext.payloads[payloadOffset] );
float lightLeakBias = params.x;
float varianceBias = params.y;
float2 evsmExponents = params.zw;
payloadOffset++;
float2 warpedDepth = ShadowMoments_WarpDepth( depth, evsmExponents );
float4 moments = SAMPLE_TEXTURE2D_ARRAY_LOD( tex, samp, tcs.xy, slice, 0.0 );
// Derivate of warping at depth
float2 depthScale = evsmExponents * warpedDepth;
float2 minVariance = depthScale * depthScale * varianceBias;
[branch]
if( fourMoments )
{
float posContrib = ShadowMoments_ChebyshevsInequality( moments.xz, warpedDepth.x, minVariance.x, lightLeakBias );
float negContrib = ShadowMoments_ChebyshevsInequality( moments.yw, warpedDepth.y, minVariance.y, lightLeakBias );
return min( posContrib, negContrib );
}
else
{
return ShadowMoments_ChebyshevsInequality( moments.xy, warpedDepth.x, minVariance.x, lightLeakBias );
}
}
//
// 1 tap MSM sampling
//
float SampleShadow_MSM_1tap( ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, uint slice, uint texIdx, uint sampIdx, bool useHamburger )
{
float3 params = asfloat( shadowContext.payloads[payloadOffset].xyz );
float lightLeakBias = params.x;
float momentBias = params.y;
float depthBias = params.z;
float depth = tcs.z + depthBias;
payloadOffset++;
float4 moments = SampleShadow_T2DA( shadowContext, texIdx, sampIdx, tcs.xy, slice );
float3 z;
float4 b;
ShadowMoments_SolveMSM( moments, depth, momentBias, z, b );
if( useHamburger )
return ShadowMoments_SolveDelta3MSM( z, b.xy, lightLeakBias );
else
return (z[1] < 0.0 || z[2] > 1.0) ? ShadowMoments_SolveDelta4MSM( z, b, lightLeakBias ) : ShadowMoments_SolveDelta3MSM( z, b.xy, lightLeakBias );
}
float SampleShadow_MSM_1tap( ShadowContext shadowContext, inout uint payloadOffset, float3 tcs, uint slice, Texture2DArray tex, SamplerState samp, bool useHamburger )
{
float3 params = asfloat( shadowContext.payloads[payloadOffset].xyz );
float lightLeakBias = params.x;
float momentBias = params.y;
float depthBias = params.z;
float depth = tcs.z + depthBias;
payloadOffset++;
float4 moments = SAMPLE_TEXTURE2D_ARRAY_LOD( tex, samp, tcs.xy, slice, 0.0 );
float3 z;
float4 b;
ShadowMoments_SolveMSM( moments, depth, momentBias, z, b );
if( useHamburger )
return ShadowMoments_SolveDelta3MSM( z, b.xy, lightLeakBias );
else
return (z[1] < 0.0 || z[2] > 1.0) ? ShadowMoments_SolveDelta4MSM( z, b, lightLeakBias ) : ShadowMoments_SolveDelta3MSM( z, b.xy, lightLeakBias );
}
//-----------------------------------------------------------------------------------------------------
// helper function to dispatch a specific shadow algorithm
float SampleShadow_SelectAlgorithm( ShadowContext shadowContext, ShadowData shadowData, inout uint payloadOffset, float3 posTC, float depthBias, uint slice, uint algorithm, uint texIdx, uint sampIdx )
{
[branch]
switch( algorithm )
{
case GPUSHADOWALGORITHM_PCF_1TAP : return SampleShadow_PCF_1tap( shadowContext, payloadOffset, posTC, depthBias, slice, texIdx, sampIdx );
case GPUSHADOWALGORITHM_PCF_9TAP : return SampleShadow_PCF_9tap_Adaptive( shadowContext, payloadOffset, shadowData.texelSizeRcp, posTC, depthBias, slice, texIdx, sampIdx );
case GPUSHADOWALGORITHM_VSM : return SampleShadow_VSM_1tap( shadowContext, payloadOffset, posTC, slice, texIdx, sampIdx );
case GPUSHADOWALGORITHM_EVSM_2 : return SampleShadow_EVSM_1tap( shadowContext, payloadOffset, posTC, slice, texIdx, sampIdx, false );
case GPUSHADOWALGORITHM_EVSM_4 : return SampleShadow_EVSM_1tap( shadowContext, payloadOffset, posTC, slice, texIdx, sampIdx, true );
case GPUSHADOWALGORITHM_MSM_HAM : return SampleShadow_MSM_1tap( shadowContext, payloadOffset, posTC, slice, texIdx, sampIdx, true );
case GPUSHADOWALGORITHM_MSM_HAUS : return SampleShadow_MSM_1tap( shadowContext, payloadOffset, posTC, slice, texIdx, sampIdx, false );
default: return 1.0;
}
}
float SampleShadow_SelectAlgorithm( ShadowContext shadowContext, ShadowData shadowData, inout uint payloadOffset, float3 posTC, float depthBias, uint slice, uint algorithm, Texture2DArray tex, SamplerComparisonState compSamp )
{
[branch]
switch( algorithm )
{
case GPUSHADOWALGORITHM_PCF_1TAP : return SampleShadow_PCF_1tap( shadowContext, payloadOffset, posTC, depthBias, slice, tex, compSamp );
case GPUSHADOWALGORITHM_PCF_9TAP : return SampleShadow_PCF_9tap_Adaptive( shadowContext, payloadOffset, shadowData.texelSizeRcp, posTC, depthBias, slice, tex, compSamp );
default: return 1.0;
}
}
float SampleShadow_SelectAlgorithm( ShadowContext shadowContext, ShadowData shadowData, inout uint payloadOffset, float3 posTC, float depthBias, uint slice, uint algorithm, Texture2DArray tex, SamplerState samp )
{
[branch]
switch( algorithm )
{
case GPUSHADOWALGORITHM_VSM : return SampleShadow_VSM_1tap( shadowContext, payloadOffset, posTC, slice, tex, samp );
case GPUSHADOWALGORITHM_EVSM_2 : return SampleShadow_EVSM_1tap( shadowContext, payloadOffset, posTC, slice, tex, samp, false );
case GPUSHADOWALGORITHM_EVSM_4 : return SampleShadow_EVSM_1tap( shadowContext, payloadOffset, posTC, slice, tex, samp, true );
case GPUSHADOWALGORITHM_MSM_HAM : return SampleShadow_MSM_1tap( shadowContext, payloadOffset, posTC, slice, tex, samp, true );
case GPUSHADOWALGORITHM_MSM_HAUS : return SampleShadow_MSM_1tap( shadowContext, payloadOffset, posTC, slice, tex, samp, false );
default: return 1.0;
}
}

15
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowUtilities.cs


case LightType.Area : gputype = GPULightType.Rectangle; shadowtype = GPUShadowType.Unknown; return false; // area lights by themselves can't be mapped to any GPU type
}
}
public static GPUShadowAlgorithm Pack( ShadowAlgorithm algo, ShadowVariant vari )
{
return (GPUShadowAlgorithm) (((int) algo << ShadowExp.ShadowConstants.Bits.k_ShadowVariant) | (int)vari);
}
public static ShadowAlgorithm ExtractAlgorithm( GPUShadowAlgorithm gpuAlgo ) { return (ShadowAlgorithm) (((int)gpuAlgo >> ShadowExp.ShadowConstants.Bits.k_ShadowVariant) & ShadowExp.ShadowConstants.Masks.k_ShadowAlgorithm ); }
public static ShadowVariant ExtractVariant( GPUShadowAlgorithm gpuAlgo ) { return (ShadowVariant ) ( (int)gpuAlgo & ShadowExp.ShadowConstants.Masks.k_ShadowVariant ); }
public static void Unpack( GPUShadowAlgorithm gpuAlgo, out ShadowAlgorithm algo, out ShadowVariant vari )
{
algo = ExtractAlgorithm( gpuAlgo );
vari = ExtractVariant( gpuAlgo );
}
public static float Asfloat( int val ) { return System.BitConverter.ToSingle( System.BitConverter.GetBytes( val ), 0 ); }
}
} // end of namespace UnityEngine.Experimental.ScriptableRenderLoop

126
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowMoments.hlsl


// Library header containing various useful functions for doing moment based shadow maps.
// Supported flavors are VSM, EVSM and MSM
// conversion helper for VSM flavors
// Chebychev's inequality (one-tailed version)
// P( x >= t ) <= pmax(t) := sigma^2 / (sigma^2 + (t - u)^2)
// for us t is depth, u is E(x) i.d. the blurred depth
float ShadowMoments_ChebyshevsInequality( float2 moments, float depth, float minVariance, float lightLeakBias )
{
// variance sig^2 = E(x^2) - E(x)^2
float variance = max( moments.y - (moments.x * moments.x), minVariance );
// probabilistic upper bound
float mD = depth - moments.x;
float p = variance / (variance + mD * mD);
#if UNITY_REVERSED_Z
p = saturate( (p - lightLeakBias) / (1.0f - lightLeakBias) );
return max( p, depth >= moments.x );
#else
p = saturate( p / (1.0f - lightLeakBias) );
return max( p, depth <= moments.x );
#endif
}
// helper for EVSM
float2 ShadowMoments_WarpDepth( float depth, float2 exponents )
{
// Rescale depth into [-1;1]
depth = 2.0 * depth - 1.0;
float pos = exp( exponents.x * depth );
float neg = -exp(-exponents.y * depth );
return float2( pos, neg );
}
// helpers for MSM
// Prepare the moments so there's little quantization error when storing the moments at half
// precision. This step becomes unnecessary if the moments are stored in 32bit floats.
float4 ShadowMoments_Encode16MSM( float depth )
{
float dsq = depth * depth;
float4 moments = { depth, dsq, depth * dsq, dsq * dsq };
float4x4 mat = { - 2.07224649f , 13.7948857237f, 0.105877704f , 9.7924062118f,
32.23703778f , -59.4683975703f, -1.9077466311f, -33.7652110555f,
-68.571074599f , 82.0359750338f, 9.3496555107f, 47.9456096605f,
39.3703274134f, -35.364903257f , -6.6543490743f, -23.9728048165f };
float4 optimized = mul( moments, mat );
optimized[0] += 0.035955884801f;
return optimized;
}
float4 ShadowMoments_Decode16MSM( float4 moments )
{
moments[0] -= 0.035955884801f;
float4x4 mat = { 0.2227744146f, 0.1549679261f, 0.1451988946f, 0.163127443f,
0.0771972861f, 0.1394629426f, 0.2120202157f, 0.2591432266f,
0.7926986636f, 0.7963415838f, 0.7258694464f, 0.6539092497f,
0.0319417555f, -0.1722823173f, -0.2758014811f, -0.3376131734f };
return mul( moments, mat );
}
void ShadowMoments_SolveMSM( float4 moments, float depth, float momentBias, out float3 z, out float4 b )
{
// Bias input data to avoid artifacts
z[0] = depth;
b = lerp( moments, 0.5.xxxx, momentBias );
// Compute a Cholesky factorization of the Hankel matrix B storing only non-trivial entries or related products
float L32D22 = mad( -b[0], b[1], b[2] );
float D22 = mad( -b[0], b[0], b[1] );
float sqDepthVar = mad( -b[1], b[1], b[3] );
float D33D22 = dot( float2( sqDepthVar, -L32D22 ), float2( D22, L32D22 ) );
float InvD22 = 1.0 / D22;
float L32 = L32D22 * InvD22;
// Obtain a scaled inverse image of bz = ( 1, z[0], z[0]*z[0] )^T
float3 c = float3( 1.0, z[0], z[0] * z[0] );
// Forward substitution to solve L * c1 = bz;
c[1] -= b.x;
c[2] -= b.y + L32 * c[1];
// Scaling to solve D * c2 = c1
c[1] *= InvD22;
c[2] *= D22 / D33D22;
// Backward substitution to solve L^T * c3 = c2
c[1] -= L32 * c[2];
c[0] -= dot( c.yz, b.xy );
// Solve the quadratic equation c[0] + c[1] * z + c[2] * z^2 to obtain solutions z[1] and z[2]
float p = c[1] / c[2];
float q = c[0] / c[2];
float D = ((p*p) * 0.25) - q;
float r = sqrt( D );
z[1] = -(p * 0.5) - r;
z[2] = -(p * 0.5) + r;
}
float ShadowMoments_SolveDelta3MSM( float3 z, float2 b, float lightLeakBias )
{
// Construct a solution composed of three Dirac-deltas and evaluate its CDF
float4 switchVal = (z[2] < z[0]) ? float4( z[1], z[0], 1.0, 1.0 )
: ((z[1] < z[0]) ? float4( z[0], z[1], 0.0, 1.0 ) : 0.0.xxxx);
float quotient = (switchVal[0] * z[2] - b[0] * (switchVal[0] + z[2]) + b[1]) / ((z[2] - switchVal[1]) * (z[0] - z[1]));
float attenuation = saturate( switchVal[2] + switchVal[3] * quotient );
#if UNITY_REVERSED_Z // probably
return saturate( (attenuation - lightLeakBias) / (1.0f - lightLeakBias) );
#else
return saturate( ((1.0 - attenuation) - lightLeakBias) / (1.0f - lightLeakBias) );
#endif
}
float ShadowMoments_SolveDelta4MSM( float3 z, float4 b, float lightLeakBias)
{
// Use a solution made of four deltas
float zFree = ((b[2] - b[1]) * z[0] + b[2] - b[3]) / ((b[1] - b[0]) * z[0] + b[1] - b[2]);
float w1Factor = (z[0] > zFree) ? 1.0 : 0.0;
float attenuation = saturate( (b[1] - b[0] + (b[2] - b[0] - (zFree + 1.0) * (b[1] - b[0])) * (zFree - w1Factor - z[0]) / (z[0] * (z[0] - zFree))) / (zFree - w1Factor) + 1.0 - b[0] );
#if UNITY_REVERSED_Z // probably
return saturate( (attenuation - lightLeakBias) / (1.0f - lightLeakBias) );
#else
return saturate( ((1.0 - attenuation) - lightLeakBias) / (1.0f - lightLeakBias) );
#endif
}

9
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowMoments.hlsl.meta


fileFormatVersion: 2
guid: fac04f0cf24b3844e8745273c0627fd1
timeCreated: 1488300302
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存