浏览代码

Merge pull request #164 from Unity-Technologies/shadows

Shadows
/Branch_Batching2
GitHub 7 年前
当前提交
5444da2d
共有 38 个文件被更改,包括 2448 次插入20 次删除
  1. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.asset
  2. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.asset.meta
  3. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs
  4. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs.meta
  5. 1
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/LightLoop.cs
  6. 3
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Lighting.hlsl
  7. 201
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.cs
  8. 7
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.hlsl
  9. 17
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassLoop.hlsl
  10. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassProducer.asset.meta
  11. 23
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl
  12. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow.meta
  13. 135
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.hlsl
  14. 1
      Assets/ScriptableRenderPipeline/ShaderLibrary/API/D3D11.hlsl
  15. 1
      Assets/ScriptableRenderPipeline/ShaderLibrary/API/Metal.hlsl
  16. 1
      Assets/ScriptableRenderPipeline/ShaderLibrary/API/PSSL.hlsl
  17. 692
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.cs
  18. 12
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.cs.meta
  19. 188
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithms.hlsl
  20. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithms.hlsl.meta
  21. 11
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithmsCustom.hlsl
  22. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowAlgorithmsCustom.hlsl.meta
  23. 354
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs
  24. 71
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs.hlsl
  25. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs.hlsl.meta
  26. 12
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs.meta
  27. 29
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowContext.hlsl
  28. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowContext.hlsl.meta
  29. 52
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowDispatch.hlsl
  30. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowDispatch.hlsl.meta
  31. 86
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowSampling.hlsl
  32. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowSampling.hlsl.meta
  33. 81
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowTexFetch.hlsl
  34. 9
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowTexFetch.hlsl.meta
  35. 88
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowUtilities.cs
  36. 12
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowUtilities.cs.meta
  37. 303
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/VectorArray.cs
  38. 12
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/VectorArray.cs.meta

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.asset


m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d440c0deec24a2f478b3e9021cb66c29, type: 3}
m_Script: {fileID: 11500000, guid: f365a473b136bef4797c7281a02cd510, type: 3}
m_Name: HDRenderPipeline
m_EditorClassIdentifier:
m_LightLoopProducer: {fileID: 11400000, guid: bf8cd9ae03ff7d54c89603e67be0bfc5,

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.asset.meta


fileFormatVersion: 2
guid: e185fecca3c73cd47a09f1092663ef32
timeCreated: 1486896330
timeCreated: 1486997164
licenseType: Pro
NativeFormatImporter:
mainObjectFileID: 11400000

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs


using (new Utilities.ProfilingSample("Build Light list", renderContext))
{
m_LightLoop.PrepareLightsForGPU(m_Owner.shadowSettings, cullResults, camera, ref m_ShadowsResult);
m_LightLoop.RenderShadows(renderContext, cullResults);
renderContext.SetupCameraProperties(camera); // Need to recall SetupCameraProperties after m_ShadowPass.Render
m_LightLoop.BuildGPULightLists(camera, renderContext, m_CameraDepthStencilBufferRT); // TODO: Use async compute here to run light culling during shadow
}
}

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs.meta


fileFormatVersion: 2
guid: d440c0deec24a2f478b3e9021cb66c29
guid: f365a473b136bef4797c7281a02cd510
timeCreated: 1483605696
licenseType: Pro
MonoImporter:

1
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/LightLoop.cs


public virtual void NewFrame() {}
public virtual void PrepareLightsForGPU(ShadowSettings shadowSettings, CullResults cullResults, Camera camera, ref ShadowOutput shadowOutput) { }
public virtual void RenderShadows( ScriptableRenderContext renderContext, CullResults cullResults ) { }
// TODO: this should not be part of the interface but for now make something working
public virtual void BuildGPULightLists(Camera camera, ScriptableRenderContext loop, RenderTargetIdentifier cameraDepthBufferRT) { }

3
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Lighting.hlsl


#include "HDRenderPipeline/Lighting/LightDefinition.cs.hlsl"
#include "HDRenderPipeline/Lighting/LightUtilities.hlsl"
#include "HDRenderPipeline/Shadow/Shadow.hlsl"
#include "HDRenderPipeline/Shadow/Shadow.hlsl"
#include "HDRenderPipeline/Material/Material.hlsl"
// LightLoop use evaluation BSDF function for light type define in Material.hlsl

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


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

#if SHADOWS_ENABLED
using ShadowExp;
class ShadowSetup : IDisposable
{
// shadow related stuff
const int k_MaxShadowDataSlots = 64;
const int k_MaxPayloadSlotsPerShadowData = 4;
ShadowmapBase[] m_Shadowmaps;
ShadowManager m_ShadowMgr;
static ComputeBuffer s_ShadowDataBuffer;
static ComputeBuffer s_ShadowPayloadBuffer;
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 ) ) );
ShadowAtlas.AtlasInit atlasInit;
atlasInit.baseInit.width = (uint) shadowSettings.shadowAtlasWidth;
atlasInit.baseInit.height = (uint) shadowSettings.shadowAtlasHeight;
atlasInit.baseInit.slices = 1;
atlasInit.baseInit.shadowmapBits = 32;
atlasInit.baseInit.shadowmapFormat = RenderTextureFormat.Shadowmap;
atlasInit.baseInit.clearColor = new Vector4( 0.0f, 0.0f, 0.0f, 0.0f );
atlasInit.baseInit.maxPayloadCount = 0;
atlasInit.baseInit.shadowSupport = ShadowmapBase.ShadowSupport.Directional;
atlasInit.shaderKeyword = null;
atlasInit.cascadeCount = shadowSettings.directionalLightCascadeCount;
atlasInit.cascadeRatios = shadowSettings.directionalLightCascades;
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 ) };
ShadowContext.SyncDel syncer = (ShadowContext sc) =>
{
// update buffers
uint offset, count;
ShadowExp.ShadowData[] sds;
sc.GetShadowDatas( out sds, out offset, out count );
Debug.Assert( offset == 0 );
s_ShadowDataBuffer.SetData( sds ); // unfortunately we can't pass an offset or count to this function
ShadowPayload[] payloads;
sc.GetPayloads( out payloads, out offset, out count );
Debug.Assert( offset == 0 );
s_ShadowPayloadBuffer.SetData( payloads );
};
// binding code. This needs to be in sync with ShadowContext.hlsl
ShadowContext.BindDel binder = (ShadowContext sc, CommandBuffer cb ) =>
{
// bind buffers
cb.SetGlobalBuffer( "_ShadowDatasExp", s_ShadowDataBuffer );
cb.SetGlobalBuffer( "_ShadowPayloads", s_ShadowPayloadBuffer );
// bind textures
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] );
// TODO: Currently samplers are hard coded in ShadowContext.hlsl, so we can't really set them here
};
ShadowContext.CtxtInit scInit;
scInit.storage.maxShadowDataSlots = k_MaxShadowDataSlots;
scInit.storage.maxPayloadSlots = k_MaxShadowDataSlots * k_MaxPayloadSlotsPerShadowData;
scInit.storage.maxTex2DArraySlots = 4;
scInit.storage.maxTexCubeArraySlots = 2;
scInit.storage.maxComparisonSamplerSlots = 2;
scInit.storage.maxSamplerSlots = 2;
scInit.dataSyncer = syncer;
scInit.resourceBinder = binder;
m_ShadowMgr = new ShadowExp.ShadowManager( shadowSettings, ref scInit, m_Shadowmaps );
shadowManager = m_ShadowMgr;
}
public void Dispose()
{
if( m_Shadowmaps != null )
{
(m_Shadowmaps[0] as ShadowAtlas).Dispose();
(m_Shadowmaps[1] as ShadowAtlas).Dispose();
m_Shadowmaps = null;
}
m_ShadowMgr = null;
Utilities.SafeRelease( s_ShadowDataBuffer );
Utilities.SafeRelease( s_ShadowPayloadBuffer );
}
}
#endif
namespace TilePass
{
//-----------------------------------------------------------------------------

const int k_TileSize = 16;
#if (SHADOWS_ENABLED)
// shadow related stuff
FrameId m_FrameId;
ShadowSetup m_ShadowSetup; // doesn't actually have to reside here, it would be enough to pass the IShadowManager in from the outside
IShadowManager m_ShadowMgr;
List<int> m_ShadowRequests = new List<int>();
Dictionary<int, int> m_ShadowIndices = new Dictionary<int,int>();
void InitShadowSystem( ShadowSettings shadowSettings )
{
m_ShadowSetup = new ShadowSetup( shadowSettings, out m_ShadowMgr );
}
void DeinitShadowSystem()
{
if( m_ShadowSetup != null )
{
m_ShadowSetup.Dispose();
m_ShadowSetup = null;
m_ShadowMgr = null;
}
}
#endif
int GetNumTileX(Camera camera)
{
return (camera.pixelWidth + (k_TileSize - 1)) / k_TileSize;

#if UNITY_EDITOR
UnityEditor.SceneView.onSceneGUIDelegate -= OnSceneGUI;
UnityEditor.SceneView.onSceneGUIDelegate += OnSceneGUI;
#endif
#if (SHADOWS_ENABLED)
InitShadowSystem(ShadowSettings.Default);
#endif
}

if (cullResults.visibleLights.Length == 0 && cullResults.visibleReflectionProbes.Length == 0)
return;
#if (SHADOWS_ENABLED)
// 0. deal with shadows
{
m_FrameId.frameCount++;
// get the indices for all lights that want to have shadows
m_ShadowRequests.Clear();
m_ShadowRequests.Capacity = cullResults.visibleLights.Length;
int lcnt = cullResults.visibleLights.Length;
for( int i = 0; i < lcnt; ++i )
{
if( cullResults.visibleLights[i].light.shadows != LightShadows.None )
m_ShadowRequests.Add( i );
}
// pass this list to a routine that assigns shadows based on some heuristic
uint shadowRequestCount = (uint) m_ShadowRequests.Count;
int[] shadowRequests = m_ShadowRequests.ToArray();
int[] shadowDataIndices;
uint originalRequestCount = shadowRequestCount;
m_ShadowMgr.ProcessShadowRequests( m_FrameId, cullResults, camera, cullResults.visibleLights,
ref shadowRequestCount, shadowRequests, out shadowDataIndices );
// update the visibleLights with the shadow information
m_ShadowIndices.Clear();
for( uint i = 0; i < shadowRequestCount; i++ )
{
m_ShadowIndices.Add( shadowRequests[i], shadowDataIndices[i] );
}
}
#endif
// 1. Count the number of lights and sort all light by category, type and volume
int directionalLightcount = 0;
int punctualLightcount = 0;

}
}
#if (SHADOWS_ENABLED)
uint shadow = m_ShadowIndices.ContainsKey( lightIndex ) ? 1u : 0;
// 5 bit (0x1F) light category, 5 bit (0x1F) GPULightType, 5 bit (0x1F) lightVolume, 1 bit for shadow casting, 16 bit index
sortKeys[sortCount++] = (uint)lightCategory << 27 | (uint)gpuLightType << 22 | (uint)lightVolumeType << 17 | shadow << 16 | (uint)lightIndex;
#else
#endif
}
Array.Sort(sortKeys);

uint sortKey = sortKeys[sortIndex];
LightCategory lightCategory = (LightCategory)((sortKey >> 27) & 0x1F);
GPULightType gpuLightType = (GPULightType)((sortKey >> 22) & 0x1F);
#if (SHADOWS_ENABLED)
LightVolumeType lightVolumeType = (LightVolumeType)((sortKey >> 17) & 0x1F);
#else
#endif
int lightIndex = (int)(sortKey & 0xFFFF);
var light = cullResults.visibleLights[lightIndex];

{
GetDirectionalLightData(shadowSettings, gpuLightType, light, additionalData, lightIndex, ref shadowOutput, ref directionalShadowcount);
#if (SHADOWS_ENABLED && SHADOWS_FIXSHADOWIDX)
// fix up shadow information
int shadowIdxDir;
if( m_ShadowIndices.TryGetValue( lightIndex, out shadowIdxDir ) )
{
var lightData = m_lightList.directionalLights[m_lightList.directionalLights.Count-1];
lightData.shadowIndex = shadowIdxDir;
m_lightList.directionalLights[m_lightList.directionalLights.Count-1] = lightData;
}
#endif
continue;
}

GetLightVolumeDataAndBound(lightCategory, gpuLightType, lightVolumeType, light, m_lightList.lights[m_lightList.lights.Count - 1], worldToView);
#if (SHADOWS_ENABLED && SHADOWS_FIXSHADOWIDX)
// fix up shadow information
int shadowIdx;
if( m_ShadowIndices.TryGetValue( lightIndex, out shadowIdx ) )
{
var lightData = m_lightList.lights[m_lightList.lights.Count-1];
lightData.shadowIndex = shadowIdx;
m_lightList.lights[m_lightList.lights.Count-1] = lightData;
}
#endif
}
// Sanity check

private void BindGlobalParams(CommandBuffer cmd, ComputeShader computeShader, int kernelIndex, Camera camera, ScriptableRenderContext loop)
{
#if (SHADOWS_ENABLED)
m_ShadowMgr.BindResources(loop);
#endif
SetGlobalPropertyRedirect(computeShader, kernelIndex, cmd);
SetGlobalTexture("_CookieTextures", m_CookieTexArray.GetTexCache());

s_ConvexBoundsBuffer.SetData(m_lightList.bounds.ToArray());
s_LightVolumeDataBuffer.SetData(m_lightList.lightVolumes.ToArray());
#if (SHADOWS_ENABLED)
// Shadows
m_ShadowMgr.SyncData();
#endif
BindGlobalParams(cmd, null, 0, camera, loop);
SetGlobalPropertyRedirect(null, 0, null);

m_mousePosition = Event.current.mousePosition;
}
#endif
public override void RenderShadows( ScriptableRenderContext renderContext, CullResults cullResults )
{
#if (SHADOWS_ENABLED)
// kick off the shadow jobs here
m_ShadowMgr.RenderShadows( m_FrameId, renderContext, cullResults, cullResults.visibleLights );
#endif
}
private void SetupRenderingForDebug(LightingDebugSettings lightDebugSettings)
{

7
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.hlsl


{
int sampleShadow;
int sampleReflection;
#ifdef SHADOWS_USE_SHADOWCTXT
ShadowContext shadowContext;
#endif
#ifndef SHADOWS_USE_SHADOWCTXT
//-----------------------------------------------------------------------------
// Shadow sampling function
// ----------------------------------------------------------------------------

return int(4.0 - dot(weights, float4(4.0, 3.0, 2.0, 1.0)));
}
float GetDirectionalShadowAttenuation(LightLoopContext lightLoopContext, float3 positionWS, int index, float3 L, float2 unPositionSS)
{

return flSum;
}
#endif
//-----------------------------------------------------------------------------
// Cookie sampling functions

17
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassLoop.hlsl


out float3 specularLighting)
{
LightLoopContext context;
#ifndef SHADOWS_USE_SHADOWCTXT
#else
context.sampleShadow = 0;
context.sampleReflection = 0;
context.shadowContext = InitShadowContext();
#endif
diffuseLighting = float3(0.0, 0.0, 0.0);
specularLighting = float3(0.0, 0.0, 0.0);

uint punctualLightStart;
uint punctualLightCount;
GetCountAndStart(posInput, LIGHTCATEGORY_PUNCTUAL, punctualLightStart, punctualLightCount);
for (i = 0; i < punctualLightCount; ++i)
for (i = 0; i < punctualLightCount; ++i)
{
float3 localDiffuseLighting, localSpecularLighting;

out float3 specularLighting)
{
LightLoopContext context;
ZERO_INITIALIZE(LightLoopContext, context);
#ifndef SHADOWS_USE_SHADOWCTXT
ZERO_INITIALIZE(LightLoopContext, context);
#else
context.sampleShadow = 0;
context.sampleReflection = 0;
context.shadowContext = InitShadowContext();
#endif
diffuseLighting = float3(0.0, 0.0, 0.0);
specularLighting = float3(0.0, 0.0, 0.0);

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassProducer.asset.meta


fileFormatVersion: 2
guid: bf8cd9ae03ff7d54c89603e67be0bfc5
timeCreated: 1485857933
timeCreated: 1486997442
licenseType: Pro
NativeFormatImporter:
mainObjectFileID: 11400000

23
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);
#else
#endif
illuminance *= shadow;
}

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

[branch] if (lightData.shadowIndex >= 0 && illuminance > 0.0)
{
float3 offset = float3(0.0, 0.0, 0.0); // GetShadowPosOffset(nDotL, normal);
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#ifdef SHADOWS_USE_SHADOWCTXT
float shadow = GetPunctualShadowAttenuation(lightLoopContext.shadowContext, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#else
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#endif
shadow = lerp(1.0, shadow, lightData.shadowDimmer);
illuminance *= shadow;

// TODO: factor out the common biased position?
float3 biasedPositionWS = positionWS + bsdfData.normalWS * bsdfData.thickness;
float3 offset = float3(0.0, 0.0, 0.0); // GetShadowPosOffset(nDotL, normal);
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, biasedPositionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#ifdef SHADOWS_USE_SHADOWCTXT
float shadow = GetPunctualShadowAttenuation(lightLoopContext.shadowContext, biasedPositionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#else
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, biasedPositionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS);
#endif
shadow = lerp(1.0, shadow, lightData.shadowDimmer);
illuminance *= shadow;

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow.meta


fileFormatVersion: 2
guid: 74dbe7f252951f340bb1038a738db89e
guid: f4cebcdbc19874c46ab4194e5ce59c67
folderAsset: yes
timeCreated: 1477395055
licenseType: Pro

135
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.hlsl


// TODO:
// - How to support a Gather sampling with such abstraction ?
// - What's belong to shadow and what's belong to renderloop ? (shadowmap size depends on the usage of atlas or not)
// - Is PunctualShadowData fixed or customizable ? Who is the owner ? Should it be pass to GetPunctualShadowAttenuation ? Sure it should...
// - Could be return by GetShadowTextureCoordinate() and pass to GetPunctualShadowAttenuation(). But in this case, who control the atlas application ?
// TODO:
// Caution: formula doesn't work as we are texture atlas...
// if (max3(abs(NDC.x), abs(NDC.y), 1.0 - texCoordXYZ.z) <= 1.0) return 1.0;
#ifndef SHADOW_HLSL
#define SHADOW_HLSL
//
// Shadow master include header.
//
// There are four relevant files for shadows.
// First ShadowContext.hlsl must declare the specific ShadowContext struct and the loader that goes along with it.
// ShadowContext loading and resource setup from C# must be in sync.
//
// Second there are two headers for shadow algorithms, whose signatures must match any of the Get...Attenuation function prototypes.
// The first header contains engine defaults, whereas the second header is empty by default. All project specific custom shadow algorithms should go in there or leave empty.
//
// Last there's a dispatcher include. By default the Get...Attenuation functions are rerouted to their default implementations. This can be overridden for each
// shadow type in the dispatcher source. For each overridden shadow type a specific define must be defined to prevent falling back to the default functions.
//
//#define SHADOWS_USE_SHADOWCTXT
#ifdef SHADOWS_USE_SHADOWCTXT
#define SHADOW_SUPPORTS_DYNAMIC_INDEXING 0 // only on >= sm 5.1
// TODO: Remove this once we've moved over to the new system. Also delete the undef at the bottom again.
#define ShadowData ShadowDataExp
#include "ShadowBase.cs.hlsl" // ShadowData definition, auto generated (don't modify)
#include "ShadowTexFetch.hlsl" // Resource sampling definitions (don't modify)
#define SHADOWCONTEXT_DECLARE( _Tex2DArraySlots, _TexCubeArraySlots, _SamplerCompSlots, _SamplerSlots ) \
\
struct ShadowContext \
{ \
StructuredBuffer<ShadowData> shadowDatas; \
StructuredBuffer<int4> payloads; \
Texture2DArray tex2DArray[_Tex2DArraySlots]; \
TextureCubeArray texCubeArray[_TexCubeArraySlots]; \
SamplerComparisonState compSamplers[_SamplerCompSlots]; \
SamplerState samplers[_SamplerSlots]; \
}; \
\
SHADOW_DEFINE_SAMPLING_FUNCS( _Tex2DArraySlots, _TexCubeArraySlots, _SamplerCompSlots, _SamplerSlots )
// Shadow context definition and initialization, i.e. resource binding (project header, must be kept in sync with C# runtime)
#include "ShadowContext.hlsl"
// helper function to extract shadowmap data from the ShadowData struct
void unpackShadowmapId( uint shadowmapId, out uint texIdx, out uint sampIdx, out float slice )
{
texIdx = (shadowmapId >> 24) & 0xff;
sampIdx = (shadowmapId >> 16) & 0xff;
slice = (float)(shadowmapId & 0xffff);
}
void unpackShadowmapId( uint shadowmapId, out uint texIdx, out uint sampIdx )
{
texIdx = (shadowmapId >> 24) & 0xff;
sampIdx = (shadowmapId >> 16) & 0xff;
}
void unpackShadowmapId( uint shadowmapId, out float slice )
{
slice = (float)(shadowmapId & 0xffff);
}
// shadow sampling prototypes
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L );
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS );
// shadow sampling prototypes with screenspace info
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L );
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS );
// wedge in the actual shadow sampling algorithms
#include "ShadowSampling.hlsl" // sampling patterns
#include "ShadowAlgorithms.hlsl" // engine default algorithms (don't modify)
#include "ShadowAlgorithmsCustom.hlsl" // project specific custom algorithms (project can modify this)
// 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 )
{
return EvalShadow_PunctualDepth(shadowContext, positionWS, shadowDataIndex, L);
}
float GetPunctualShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
{
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
}
// directional light shadows
float GetDirectionalShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
{
return EvalShadow_CascadedDepth( shadowContext, positionWS, shadowDataIndex, L );
}
float GetDirectionalShadowAttenuationDefault( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
{
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
}
// include project specific shadow dispatcher. If this file is not empty, it MUST define which default shadows it's overriding
#include "ShadowDispatch.hlsl"
// if shadow dispatch is empty we'll fall back to default shadow sampling implementations
#ifndef SHADOW_DISPATCH_USE_CUSTOM_PUNCTUAL
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
{
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
}
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
{
return GetPunctualShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L, unPositionSS );
}
#endif
#ifndef SHADOW_DISPATCH_USE_CUSTOM_DIRECTIONAL
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L )
{
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L );
}
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
{
return GetDirectionalShadowAttenuationDefault( shadowContext, positionWS, shadowDataIndex, L, unPositionSS );
}
#endif
#undef ShadowData // TODO: Remove this once we've moved over to the new system. Also delete the define at the top again.
#endif // SHADOWS_USE_SHADOWCTXT
#endif // SHADOW_HLSL

1
Assets/ScriptableRenderPipeline/ShaderLibrary/API/D3D11.hlsl


#define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z)
#define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z)
#define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord3).xyz, (coord3).w)
#define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w)
#define TEXTURE2D_HALF TEXTURE2D
#define TEXTURE2D_FLOAT TEXTURE2D

1
Assets/ScriptableRenderPipeline/ShaderLibrary/API/Metal.hlsl


#define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z)
#define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z)
#define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord3).xyz, (coord3).w)
#define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w)
#define TEXTURE2D_HALF TEXTURE2D
#define TEXTURE2D_FLOAT TEXTURE2D

1
Assets/ScriptableRenderPipeline/ShaderLibrary/API/PSSL.hlsl


#define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z)
#define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z)
#define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord3).xyz, (coord3).w)
#define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w)
#define TEXTURE2D_HALF TEXTURE2D
#define TEXTURE2D_FLOAT TEXTURE2D

692
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.cs


using UnityEngine.Rendering;
using System;
namespace UnityEngine.Experimental.Rendering.HDPipeline
{
// temporary namespace
namespace ShadowExp
{
using ShadowRequestVector = VectorArray<ShadowmapBase.ShadowRequest>;
using ShadowDataVector = VectorArray<ShadowData>;
using ShadowPayloadVector = VectorArray<ShadowPayload>;
using ShadowIndicesVector = VectorArray<int>;
// Standard shadow map atlas implementation using one large shadow map
public class ShadowAtlas : ShadowmapBase, IDisposable
{
public const uint k_MaxCascadesInShader = 4;
protected readonly RenderTexture m_Shadowmap;
protected readonly RenderTargetIdentifier m_ShadowmapId;
protected readonly int m_TempDepthId;
protected VectorArray<CachedEntry> m_EntryCache = new VectorArray<CachedEntry>( 0, true );
protected uint m_ActiveEntriesCount;
protected FrameId m_FrameId;
protected string m_ShaderKeyword;
protected int m_CascadeCount;
protected Vector3 m_CascadeRatios;
protected uint m_TexSlot;
protected uint m_SampSlot;
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 struct Key
{
public int id;
public uint faceIdx;
public int visibleIdx;
public uint shadowDataIdx;
}
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 bool IsValid() { return viewport.width > 0 && viewport.height > 0; }
}
protected struct CachedEntry : IComparable<CachedEntry>
{
public Key key;
public Data current;
public Data previous;
public int CompareTo( CachedEntry other )
{
if (current.viewport.height != other.current.viewport.height)
return current.viewport.height > other.current.viewport.height ? -1 : 1;
if (current.viewport.width != other.current.viewport.width)
return current.viewport.width > other.current.viewport.width ? -1 : 1;
if( key.id != other.key.id )
return key.id < other.key.id ? -1 : 1;
return key.faceIdx != other.key.faceIdx ? (key.faceIdx < other.key.faceIdx ? -1 : 1) : 0;
}
};
public struct AtlasInit
{
public BaseInit baseInit; // the base class's initializer
public string shaderKeyword; // the global shader keyword to use when rendering the shadowmap
public int cascadeCount; // the number of cascades to use (these are global in ShadowSettings for now for some reason)
public Vector3 cascadeRatios; // cascade split ratios
}
public ShadowAtlas( ref AtlasInit init ) : base( ref init.baseInit )
{
m_Shadowmap = new RenderTexture( (int) m_Width, (int) m_Height, (int) m_ShadowmapBits, m_ShadowmapFormat, RenderTextureReadWrite.Linear );
m_Shadowmap.dimension = TextureDimension.Tex2DArray;
m_Shadowmap.volumeDepth = (int) m_Slices;
m_ShadowmapId = new RenderTargetIdentifier( m_Shadowmap );
if( !IsNativeDepth() )
{
m_TempDepthId = Shader.PropertyToID( "Temporary Shadowmap Depth" );
}
Initialize( init );
}
public void Initialize( AtlasInit init )
{
m_ShaderKeyword = init.shaderKeyword;
m_CascadeCount = init.cascadeCount;
m_CascadeRatios = init.cascadeRatios;
}
override public void ReserveSlots( ShadowContextStorage sc )
{
m_TexSlot = sc.RequestTex2DArraySlot();
m_SampSlot = IsNativeDepth() ? sc.RequestSamplerSlot( m_CompSamplerState ) : sc.RequestSamplerSlot( m_SamplerState );
}
override public void Fill( ShadowContextStorage cs )
{
cs.SetTex2DArraySlot( m_TexSlot, m_ShadowmapId );
}
public void Dispose()
{
// TODO: clean up resources if necessary
}
override public bool Reserve( FrameId frameId, ref ShadowData shadowData, ShadowRequest sr, uint width, uint height, ref VectorArray<ShadowData> entries, ref VectorArray<ShadowPayload> payload, VisibleLight[] lights )
{
for( uint i = 0, cnt = sr.facecount; i < cnt; ++i )
{
m_TmpWidths[i] = width;
m_TmpHeights[i] = height;
}
return Reserve( frameId, ref shadowData, sr, m_TmpWidths, m_TmpHeights, ref entries, ref payload, lights );
}
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 )
{
ShadowData sd = shadowData;
ShadowData dummy = new ShadowData();
if( sr.shadowType != GPUShadowType.Point && sr.shadowType != GPUShadowType.Spot && sr.shadowType != GPUShadowType.Directional )
return false;
if( sr.shadowType == GPUShadowType.Directional )
{
for( uint i = 0; i < k_MaxCascadesInShader; ++i )
m_TmpSplits[i].Set( 0.0f, 0.0f, 0.0f, float.NegativeInfinity );
}
Key key;
key.id = sr.instanceId;
key.faceIdx = 0;
key.visibleIdx = (int)sr.index;
key.shadowDataIdx = entries.Count();
uint originalEntryCount = entries.Count();
uint originalPayloadCount = payload.Count();
uint originalActiveEntries = m_ActiveEntriesCount;
uint facecnt = sr.facecount;
uint facemask = sr.facemask;
uint bit = 1;
int resIdx = 0;
entries.Reserve( 6 );
float nearPlaneOffset = QualitySettings.shadowNearPlaneOffset;
while ( facecnt > 0 )
{
if( (bit & facemask) != 0 )
{
uint width = widths[resIdx];
uint height = heights[resIdx];
uint ceIdx;
if( !Alloc( frameId, key, width, height, out ceIdx, payload ) )
{
entries.Purge( entries.Count() - originalEntryCount );
payload.Purge( payload.Count() - originalPayloadCount );
uint added = m_ActiveEntriesCount - originalActiveEntries;
for( uint i = originalActiveEntries; i < m_ActiveEntriesCount; ++i )
m_EntryCache.Swap( i, m_EntryCache.Count()-i-1 );
m_EntryCache.Purge( added, Free );
m_ActiveEntriesCount = originalActiveEntries;
return false;
}
// read
CachedEntry ce = m_EntryCache[ceIdx];
// modify
Matrix4x4 vp;
if( sr.shadowType == GPUShadowType.Point )
vp = ShadowUtils.ExtractPointLightMatrix( lights[sr.index], key.faceIdx, 2.0f, out ce.current.view, out ce.current.proj, out ce.current.lightDir, out ce.current.splitData, m_CullResults, (int) sr.index );
else if( sr.shadowType == GPUShadowType.Spot )
vp = ShadowUtils.ExtractSpotLightMatrix( lights[sr.index], out ce.current.view, out ce.current.proj, out ce.current.lightDir, out ce.current.splitData );
else if( sr.shadowType == GPUShadowType.Directional )
{
vp = ShadowUtils.ExtractDirectionalLightMatrix( lights[sr.index], key.faceIdx, m_CascadeCount, m_CascadeRatios, nearPlaneOffset, width, height, out ce.current.view, out ce.current.proj, out ce.current.lightDir, out ce.current.splitData, m_CullResults, (int) sr.index );
m_TmpSplits[key.faceIdx] = ce.current.splitData.cullingSphere;
m_TmpSplits[key.faceIdx].w *= ce.current.splitData.cullingSphere.w;
}
else
vp = Matrix4x4.identity; // should never happen, though
// write :(
m_EntryCache[ceIdx] = ce;
sd.worldToShadow = vp.transpose; // apparently we need to transpose matrices that are sent to HLSL
sd.scaleOffset = new Vector4( ce.current.viewport.width * m_WidthRcp, ce.current.viewport.height * m_HeightRcp, ce.current.viewport.x, ce.current.viewport.y );
sd.texelSizeRcp = new Vector2( m_WidthRcp, m_HeightRcp );
sd.PackShadowmapId( m_TexSlot, m_SampSlot, ce.current.slice );
sd.shadowType = sr.shadowType;
sd.payloadOffset = payload.Count();
entries.AddUnchecked(sd);
resIdx++;
facecnt--;
key.shadowDataIdx++;
}
else
{
// we push a dummy face in, otherwise we'd need a mapping from face index to shadowData in the shader as well
entries.AddUnchecked( dummy );
}
key.faceIdx++;
bit <<= 1;
}
if (sr.shadowType == GPUShadowType.Directional)
{
ShadowPayload sp = new ShadowPayload();
payload.Reserve( k_MaxCascadesInShader );
for( uint i = 0; i < k_MaxCascadesInShader; i++ )
{
sp.Set( m_TmpSplits[i] );
payload.AddUnchecked( sp );
}
}
return true;
}
override public bool ReserveFinalize( FrameId frameId, ref VectorArray<ShadowData> entries, ref VectorArray<ShadowPayload> payload )
{
if( Layout() )
{
// patch up the shadow data contents with the result of the layouting step
for( uint i = 0; i < m_ActiveEntriesCount; ++i )
{
CachedEntry ce = m_EntryCache[i];
ShadowData sd = entries[ce.key.shadowDataIdx];
// update the shadow data with the actual result of the layouting step
sd.scaleOffset = new Vector4( ce.current.viewport.width * m_WidthRcp, ce.current.viewport.height * m_HeightRcp, ce.current.viewport.x * m_WidthRcp, ce.current.viewport.y * m_HeightRcp );
sd.PackShadowmapId( m_TexSlot, m_SampSlot, ce.current.slice );
// write back the correct results
entries[ce.key.shadowDataIdx] = sd;
}
m_EntryCache.Purge(m_EntryCache.Count() - m_ActiveEntriesCount, (CachedEntry entry) => { Free(entry); });
return true;
}
m_ActiveEntriesCount = 0;
m_EntryCache.Reset( (CachedEntry entry) => { Free(entry); });
return false;
}
virtual protected void PreUpdate( FrameId frameId, CommandBuffer cb, uint rendertargetSlice )
{
cb.SetRenderTarget( m_ShadowmapId, 0, (CubemapFace) 0, (int) rendertargetSlice );
if( !IsNativeDepth() )
{
cb.GetTemporaryRT(m_TempDepthId, (int)m_Width, (int)m_Height, (int)m_ShadowmapBits, FilterMode.Bilinear, RenderTextureFormat.Shadowmap, RenderTextureReadWrite.Default);
cb.SetRenderTarget( new RenderTargetIdentifier( m_TempDepthId ) );
}
cb.ClearRenderTarget( true, !IsNativeDepth(), m_ClearColor );
}
override public void Update( FrameId frameId, ScriptableRenderContext renderContext, CullResults cullResults, VisibleLight[] lights )
{
var profilingSample = new Utilities.ProfilingSample("Shadowmap" + m_TexSlot, renderContext);
if (!string.IsNullOrEmpty( m_ShaderKeyword ) )
{
var cb = new CommandBuffer();
cb.name = "Shadowmap.EnableShadowKeyword";
cb.EnableShaderKeyword(m_ShaderKeyword);
renderContext.ExecuteCommandBuffer( cb );
cb.Dispose();
}
// loop for generating each individual shadowmap
uint curSlice = uint.MaxValue;
Bounds bounds;
DrawShadowsSettings dss = new DrawShadowsSettings( cullResults, 0 );
for( uint i = 0; i < m_ActiveEntriesCount; ++i )
{
if( !cullResults.GetShadowCasterBounds( m_EntryCache[i].key.visibleIdx, out bounds ) )
continue;
var cb = new CommandBuffer();
uint entrySlice = m_EntryCache[i].current.slice;
if( entrySlice != curSlice )
{
Debug.Assert( curSlice == uint.MaxValue || entrySlice >= curSlice, "Entries in the entry cache are not ordered in slice order." );
cb.name = "Shadowmap.Update.Slice" + entrySlice;
if( curSlice != uint.MaxValue )
{
PostUpdate( frameId, cb, curSlice );
}
curSlice = entrySlice;
PreUpdate( frameId, cb, curSlice );
}
cb.name = "Shadowmap.Update - slice: " + curSlice + ", vp.x: " + m_EntryCache[i].current.viewport.x + ", vp.y: " + m_EntryCache[i].current.viewport.y + ", vp.w: " + m_EntryCache[i].current.viewport.width + ", vp.h: " + m_EntryCache[i].current.viewport.height;
cb.SetViewport( m_EntryCache[i].current.viewport );
cb.SetViewProjectionMatrices( m_EntryCache[i].current.view, m_EntryCache[i].current.proj );
cb.SetGlobalVector( "g_vLightDirWs", m_EntryCache[i].current.lightDir );
renderContext.ExecuteCommandBuffer( cb );
cb.Dispose();
dss.lightIndex = m_EntryCache[i].key.visibleIdx;
dss.splitData = m_EntryCache[i].current.splitData;
renderContext.DrawShadows( ref dss ); // <- if this was a call on the commandbuffer we would get away with using just once commandbuffer for the entire shadowmap, instead of one per face
}
// post update
if( !string.IsNullOrEmpty( m_ShaderKeyword ) )
{
var cb = new CommandBuffer();
cb.name = "Shadowmap.DisableShaderKeyword";
cb.DisableShaderKeyword( m_ShaderKeyword );
renderContext.ExecuteCommandBuffer( cb );
cb.Dispose();
}
m_ActiveEntriesCount = 0;
profilingSample.Dispose();
}
virtual protected void PostUpdate( FrameId frameId, CommandBuffer cb, uint rendertargetSlice )
{
if( !IsNativeDepth() )
cb.ReleaseTemporaryRT( m_TempDepthId );
}
protected bool Alloc( FrameId frameId, Key key, uint width, uint height, out uint cachedEntryIdx, VectorArray<ShadowPayload> payload )
{
CachedEntry ce = new CachedEntry();
ce.key = key;
ce.current.frameId = frameId;
ce.current.contentHash = -1;
ce.current.slice = 0;
ce.current.viewport = new Rect( 0, 0, width, height );
uint idx;
if ( m_EntryCache.FindFirst( out idx, ref key, (ref Key k, ref CachedEntry entry) => { return k.id == entry.key.id && k.faceIdx == entry.key.faceIdx; } ) )
{
if( m_EntryCache[idx].current.viewport.width == width && m_EntryCache[idx].current.viewport.height == height )
{
ce.previous = m_EntryCache[idx].current;
m_EntryCache[idx] = ce;
cachedEntryIdx = m_ActiveEntriesCount;
m_EntryCache.SwapUnchecked( m_ActiveEntriesCount++, idx );
return true;
}
else
{
m_EntryCache.SwapUnchecked( idx, m_EntryCache.Count()-1 );
m_EntryCache.Purge( 1, Free );
}
}
idx = m_EntryCache.Count();
m_EntryCache.Add( ce );
cachedEntryIdx = m_ActiveEntriesCount;
m_EntryCache.SwapUnchecked( m_ActiveEntriesCount++, idx );
return true;
}
protected bool Layout()
{
VectorArray<CachedEntry> tmp = m_EntryCache.Subrange( 0, m_ActiveEntriesCount );
tmp.Sort();
float curx = 0, cury = 0, curh = 0, xmax = m_Width, ymax = m_Height;
uint curslice = 0;
for (uint i = 0; i < m_ActiveEntriesCount; ++i)
{
// shadow atlas layouting
CachedEntry ce = m_EntryCache[i];
Rect vp = ce.current.viewport;
if( curx + vp.width > xmax )
{
curx = 0;
cury += curh;
}
if( curx + vp.width > xmax || cury + curh > ymax )
{
curslice++;
curx = 0;
cury = 0;
}
if( curx + vp.width > xmax || cury + curh > ymax || curslice == m_Slices )
{
Debug.LogError( "ERROR! Shadow atlasing failed." );
return false;
}
vp.x = curx;
vp.y = cury;
ce.current.viewport = vp;
ce.current.slice = curslice;
m_EntryCache[i] = ce;
curx += vp.width;
curh = curh >= vp.height ? curh : vp.height;
}
return true;
}
protected void Free( CachedEntry ce )
{
// Nothing to do for this implementation here, as the atlas is reconstructed each frame, instead of keeping state across frames
}
}
// -------------------------------------------------------------------------------------------------------------------------------------------------
//
// ShadowManager
//
// -------------------------------------------------------------------------------------------------------------------------------------------------
// Standard shadow manager
public class ShadowManager : ShadowManagerBase
{
protected class ShadowContextAccess : ShadowContext
{
public ShadowContextAccess( ref ShadowContext.CtxtInit initializer ) : base( ref initializer ) { }
// unfortunately ref returns are only a C# 7.0 feature
public VectorArray<ShadowData> shadowDatas { get { return m_ShadowDatas; } set { m_ShadowDatas = value; } }
public VectorArray<ShadowPayload> payloads { get { return m_Payloads; } set { m_Payloads = value; } }
}
private const int k_MaxShadowmapPerType = 4;
private ShadowSettings m_ShadowSettings;
private ShadowmapBase[] m_Shadowmaps;
private ShadowmapBase[,] m_ShadowmapsPerType = new ShadowmapBase[(int)GPUShadowType.MAX, k_MaxShadowmapPerType];
private ShadowContextAccess m_ShadowCtxt;
private int[,] m_MaxShadows = new int[(int)GPUShadowType.MAX,2];
// The following vectors are just temporary helpers to avoid reallocation each frame. Contents are not stable.
private VectorArray<long> m_TmpSortKeys = new VectorArray<long>( 0, false );
private ShadowRequestVector m_TmpRequests = new ShadowRequestVector( 0, false );
// The following vector holds data that are returned to the caller so it can be sent to GPU memory in some form. Contents are stable in between calls to ProcessShadowRequests.
private ShadowIndicesVector m_ShadowIndices = new ShadowIndicesVector( 0, false );
public ShadowManager( ShadowSettings shadowSettings, ref ShadowContext.CtxtInit ctxtInitializer, ShadowmapBase[] shadowmaps )
{
m_ShadowSettings = shadowSettings;
m_ShadowCtxt = new ShadowContextAccess( ref ctxtInitializer );
Debug.Assert( shadowmaps != null && shadowmaps.Length > 0 );
m_Shadowmaps = shadowmaps;
foreach( var sm in shadowmaps )
{
sm.ReserveSlots( m_ShadowCtxt );
ShadowmapBase.ShadowSupport smsupport = sm.QueryShadowSupport();
for( int i = 0, bit = 1; i < (int) GPUShadowType.MAX; ++i, bit <<= 1 )
{
if( ((int)smsupport & bit) == 0 )
continue;
for( int idx = 0; i < k_MaxShadowmapPerType; ++idx )
{
if( m_ShadowmapsPerType[i,idx] == null )
{
m_ShadowmapsPerType[i,idx] = sm;
break;
}
}
Debug.Assert( m_ShadowmapsPerType[i,k_MaxShadowmapPerType-1] == null || m_ShadowmapsPerType[i,k_MaxShadowmapPerType-1] == sm,
"Only up to " + k_MaxShadowmapPerType + " are allowed per light type. If more are needed then increase ShadowManager.k_MaxShadowmapPerType" );
}
}
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;
}
public override void ProcessShadowRequests( FrameId frameId, CullResults cullResults, Camera camera, VisibleLight[] lights, ref uint shadowRequestsCount, int[] shadowRequests, out int[] shadowDataIndices )
{
shadowDataIndices = null;
// TODO:
// Cached the cullResults here so we don't need to pass them around.
// Allocate needs to pass them to the shadowmaps, as the ShadowUtil functions calculating view/proj matrices need them to call into C++ land.
// Ideally we can get rid of that at some point, then we wouldn't need to cache them here, anymore.
foreach( var sm in m_Shadowmaps )
{
sm.Assign( cullResults );
}
if( shadowRequestsCount == 0 || lights == null || shadowRequests == null )
{
shadowRequestsCount = 0;
return;
}
// first sort the shadow casters according to some priority
PrioritizeShadowCasters( camera, lights, shadowRequestsCount, shadowRequests );
// next prune them based on some logic
VectorArray<int> requestedShadows = new VectorArray<int>( shadowRequests, 0, shadowRequestsCount, false );
m_TmpRequests.Reset( shadowRequestsCount );
uint totalGranted;
PruneShadowCasters( camera, lights, ref requestedShadows, ref m_TmpRequests, out totalGranted );
// if there are no shadow casters at this point -> bail
if( totalGranted == 0 )
{
shadowRequestsCount = 0;
return;
}
// TODO: Now would be a good time to kick off the culling jobs for the granted requests - but there's no way to control that at the moment.
// finally go over the lights deemed shadow casters and try to fit them into the shadow map
// shadowmap allocation must succeed at this point.
m_ShadowCtxt.ClearData();
ShadowDataVector shadowVector = m_ShadowCtxt.shadowDatas;
ShadowPayloadVector payloadVector = m_ShadowCtxt.payloads;
m_ShadowIndices.Reset( m_TmpRequests.Count() );
AllocateShadows( frameId, lights, totalGranted, ref m_TmpRequests, ref m_ShadowIndices, ref shadowVector, ref payloadVector );
Debug.Assert( m_TmpRequests.Count() == m_ShadowIndices.Count() );
m_ShadowCtxt.shadowDatas = shadowVector;
m_ShadowCtxt.payloads = payloadVector;
// and set the output parameters
uint offset;
shadowDataIndices = m_ShadowIndices.AsArray( out offset, out shadowRequestsCount );
}
protected override void PrioritizeShadowCasters( Camera camera, VisibleLight[] lights, uint shadowRequestsCount, int[] shadowRequests )
{
// this function simply looks at the projected area on the screen, ignoring all light types and shapes
m_TmpSortKeys.Reset( shadowRequestsCount );
for( int i = 0; i < shadowRequestsCount; ++i )
{
int vlidx = shadowRequests[i];
VisibleLight vl = lights[vlidx];
Light l = vl.light;
// use the screen rect as a measure of importance
float area = vl.screenRect.width * vl.screenRect.height;
long val = ShadowUtils.Asint( area );
val <<= 32;
val |= (long) vlidx;
m_TmpSortKeys.AddUnchecked( val );
}
m_TmpSortKeys.Sort();
m_TmpSortKeys.ExtractTo( shadowRequests, 0, out shadowRequestsCount, delegate(long key) { return (int) (key & 0xffffffff); } );
}
protected override void PruneShadowCasters( Camera camera, VisibleLight[] lights, ref VectorArray<int> shadowRequests, ref ShadowRequestVector requestsGranted, out uint totalRequestCount )
{
Debug.Assert( shadowRequests.Count() > 0 );
// at this point the array is sorted in order of some importance determined by the prioritize function
requestsGranted.Reserve( shadowRequests.Count() );
totalRequestCount = 0;
ShadowmapBase.ShadowRequest sreq = new ShadowmapBase.ShadowRequest();
uint totalSlots = ResetMaxShadows();
// there's a 1:1 mapping between the index in the shadowRequests array and the element in requestsGranted at the same index.
// if the prune function skips requests it must make sure that the array is still compact
m_TmpSortKeys.Reset( shadowRequests.Count() );
for( uint i = 0, count = shadowRequests.Count(); i < count && totalSlots > 0; ++i )
{
int requestIdx = shadowRequests[i];
VisibleLight vl = lights[requestIdx];
bool add = false;
int facecount = 0;
GPUShadowType shadowType = GPUShadowType.Point;
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;
}
if( add )
{
sreq.instanceId = vl.light.GetInstanceID();
sreq.index = (uint) requestIdx;
sreq.facemask = (uint) (1 << facecount) - 1;
sreq.shadowType = shadowType;
totalRequestCount += (uint) facecount;
requestsGranted.AddUnchecked( sreq );
totalSlots--;
}
else
m_TmpSortKeys.AddUnchecked( requestIdx );
}
// make sure that shadowRequests contains all light indices that are going to cast a shadow first, then the rest
shadowRequests.Reset();
requestsGranted.ExtractTo( ref shadowRequests, (ShadowmapBase.ShadowRequest request) => { return (int) request.index; } );
m_TmpSortKeys.ExtractTo( ref shadowRequests, (long idx) => { return (int) idx; } );
}
protected override void AllocateShadows( FrameId frameId, VisibleLight[] lights, uint totalGranted, ref ShadowRequestVector grantedRequests, ref ShadowIndicesVector shadowIndices, ref ShadowDataVector shadowDatas, ref ShadowPayloadVector shadowmapPayload )
{
ShadowData sd = new ShadowData();
shadowDatas.Reserve( totalGranted );
shadowIndices.Reserve( grantedRequests.Count() );
for( uint i = 0, cnt = grantedRequests.Count(); i < cnt; ++i )
{
VisibleLight vl = lights[grantedRequests[i].index];
Light l = vl.light;
AdditionalLightData ald = l.GetComponent<AdditionalLightData>();
// 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;
shadowIndices.AddUnchecked( (int) shadowDatas.Count() );
int smidx = 0;
while( smidx < k_MaxShadowmapPerType )
{
if( m_ShadowmapsPerType[(int)shadowtype,smidx].Reserve( frameId, ref sd, grantedRequests[i], (uint) ald.shadowResolution, (uint) ald.shadowResolution, ref shadowDatas, ref shadowmapPayload, lights ) )
break;
smidx++;
}
if( smidx == k_MaxShadowmapPerType )
throw new ArgumentException("The requested shadows do not fit into any shadowmap.");
}
// final step for shadowmaps that only gather data during the previous loop and do the actual allocation once they have all the data.
foreach( var sm in m_Shadowmaps )
{
if( !sm.ReserveFinalize( frameId, ref shadowDatas, ref shadowmapPayload ) )
throw new ArgumentException( "Shadow allocation failed in the ReserveFinalize step." );
}
}
public override void RenderShadows( FrameId frameId, ScriptableRenderContext renderContext, CullResults cullResults, VisibleLight[] lights )
{
using (new Utilities.ProfilingSample("Render Shadows Exp", renderContext))
{
foreach( var sm in m_Shadowmaps )
{
sm.Update( frameId, renderContext, cullResults, lights );
}
}
}
public override void SyncData()
{
m_ShadowCtxt.SyncData();
}
public override void BindResources( ScriptableRenderContext renderContext )
{
foreach( var sm in m_Shadowmaps )
{
sm.Fill( m_ShadowCtxt );
}
CommandBuffer cb = new CommandBuffer(); // <- can we just keep this around or does this have to be newed every frame?
cb.name = "Bind resources to GPU";
m_ShadowCtxt.BindResources( cb );
renderContext.ExecuteCommandBuffer( cb );
cb.Dispose();
}
// resets the shadow slot counters and returns the sum of all slots
private uint ResetMaxShadows()
{
int total = 0;
for( int i = 0; i < (int) GPUShadowType.MAX; ++i )
{
m_MaxShadows[i,0] = m_MaxShadows[i,1];
total += m_MaxShadows[i,1];
}
return total > 0 ? (uint) total : 0;
}
}
} // end of temporary namespace ShadowExp
} // end of namespace UnityEngine.Experimental.ScriptableRenderLoop

12
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/Shadow.cs.meta


fileFormatVersion: 2
guid: 6931cfbd757a1864f8822b5399719960
timeCreated: 1485511432
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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


// Various shadow algorithms
// 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.
// function called by spot, point and directional eval routines to calculate shadow coordinates
float3 EvalShadow_GetTexcoords( ShadowData sd, float3 positionWS )
{
float4 posCS = mul( float4( positionWS, 1.0 ), sd.worldToShadow );
// apply a bias
posCS.z -= sd.bias;
float3 posNDC = posCS.xyz / posCS.w;
// calc TCs
float3 posTC = posNDC * 0.5 + 0.5;
posTC.xy = posTC.xy * sd.scaleOffset.xy + sd.scaleOffset.zw;
#if UNITY_REVERSED_Z
posTC.z = 1.0 - posTC.z;
#endif
return posTC;
}
//
// Point shadows
//
float EvalShadow_PointDepth( ShadowContext shadowContext, 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
uint texIdx, sampIdx;
float slice;
unpackShadowmapId( sd.id, texIdx, sampIdx, slice );
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, 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 );
}
//
// Spot shadows
//
float EvalShadow_SpotDepth( ShadowContext shadowContext, 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
uint texIdx, sampIdx;
float slice;
unpackShadowmapId( sd.id, texIdx, sampIdx, slice );
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, 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 );
}
//
// Punctual shadows for Point and Spot
//
float EvalShadow_PunctualDepth( ShadowContext shadowContext, 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
uint texIdx, sampIdx;
float slice;
unpackShadowmapId( sd.id, texIdx, sampIdx, slice );
return SampleShadow_PCF_1tap( shadowContext, posTC, slice, 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 );
}
//
// Directional shadows (cascaded shadow map)
//
uint EvalShadow_GetSplitSphereIndexForDirshadows( float3 positionWS, float4 dirShadowSplitSpheres[4] )
{
float3 fromCenter0 = positionWS.xyz - dirShadowSplitSpheres[0].xyz;
float3 fromCenter1 = positionWS.xyz - dirShadowSplitSpheres[1].xyz;
float3 fromCenter2 = positionWS.xyz - dirShadowSplitSpheres[2].xyz;
float3 fromCenter3 = positionWS.xyz - dirShadowSplitSpheres[3].xyz;
float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));
float4 dirShadowSplitSphereSqRadii;
dirShadowSplitSphereSqRadii.x = dirShadowSplitSpheres[0].w;
dirShadowSplitSphereSqRadii.y = dirShadowSplitSpheres[1].w;
dirShadowSplitSphereSqRadii.z = dirShadowSplitSpheres[2].w;
dirShadowSplitSphereSqRadii.w = dirShadowSplitSpheres[3].w;
float4 weights = float4( distances2 < dirShadowSplitSphereSqRadii );
weights.yzw = saturate( weights.yzw - weights.xyz );
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 offset = GetPayloadOffset( shadowContext.shadowDatas[index] );
splitSpheres[0] = asfloat( shadowContext.payloads[offset + 0] );
splitSpheres[1] = asfloat( shadowContext.payloads[offset + 1] );
splitSpheres[2] = asfloat( shadowContext.payloads[offset + 2] );
splitSpheres[3] = asfloat( shadowContext.payloads[offset + 3] );
}
float EvalShadow_CascadedDepth( ShadowContext shadowContext, 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
uint texIdx, sampIdx;
float slice;
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);
return SampleShadow_PCF_9tap_Adaptive( shadowContext, sd.texelSizeRcp, posTC, slice, tex, compSamp );
}

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


fileFormatVersion: 2
guid: 9900a5b8191991b4d9e2c37413d85dc3
timeCreated: 1485511902
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

11
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 )
{
return 1.0;
}
float EvalShadow_CascadedMomentum( ShadowContext shadowContext, float3 positionWS, int shadowDataIndex, float3 L, float2 unPositionSS )
{
return 1.0;
}

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


fileFormatVersion: 2
guid: 1163a3730c1812748b5cc5a7ecfbaf0c
timeCreated: 1485511901
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

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


using UnityEngine.Rendering;
using System;
namespace UnityEngine.Experimental.Rendering.HDPipeline
{
[GenerateHLSL]
public enum GPUShadowType
{
Point,
Spot,
Directional,
MAX,
Unknown = MAX,
All = Point | Spot | Directional
};
[GenerateHLSL]
public enum GPUShadowSampling
{
PCF_1tap,
PCF_9Taps_Adaptive,
VSM_1tap,
MSM_1tap
};
namespace ShadowExp // temporary namespace until everything can be merged into the HDPipeline
{
// This is the struct passed into shaders
[GenerateHLSL]
public struct ShadowData
{
// shadow texture related params (need to be set by ShadowmapBase and derivatives)
public Matrix4x4 worldToShadow; // to light space matrix
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 payloadOffset; // if this shadow type requires additional data it can be fetched from a global Buffer<uint> at payloadOffset.
// light related params (need to be set via ShadowMgr and derivatives)
public GPULightType lightType; // the light type
public float bias; // bias setting
public float quality; // some quality parameters
public void PackShadowmapId( uint texIdx, uint sampIdx, uint slice )
{
Debug.Assert( texIdx <= 0xff );
Debug.Assert( sampIdx <= 0xff );
Debug.Assert( slice <= 0xffff );
id = texIdx << 24 | sampIdx << 16 | slice;
}
};
public struct FrameId
{
public int frameCount;
public float deltaT;
}
// -------------- Begin temporary structs that need to be replaced at some point ---------------
public struct SamplerState
{
// TODO: this should either contain the description for a sampler, or be replaced by a struct that does
public static bool operator ==( SamplerState lhs, SamplerState rhs ) { return false; }
public static bool operator !=( SamplerState lhs, SamplerState rhs ) { return true; }
public override bool Equals( object obj ) { return (obj is SamplerState) && (SamplerState) obj == this; }
public override int GetHashCode() { /* TODO: implement this at some point */ throw new NotImplementedException(); }
}
public struct ComparisonSamplerState
{
// TODO: this should either contain the description for a comparison sampler, or be replaced by a struct that does
public static bool operator ==(ComparisonSamplerState lhs, ComparisonSamplerState rhs) { return false; }
public static bool operator !=(ComparisonSamplerState lhs, ComparisonSamplerState rhs) { return true; }
public override bool Equals( object obj ) { return (obj is ComparisonSamplerState) && (ComparisonSamplerState) obj == this; }
public override int GetHashCode() { /* TODO: implement this at some point */ throw new NotImplementedException(); }
}
// -------------- End temporary structs that need to be replaced at some point ---------------
public struct ShadowPayload
{
public int p0;
public int p1;
public int p2;
public int p3;
public void Set( float v0, float v1, float v2, float v3 )
{
p0 = ShadowUtils.Asint( v0 );
p1 = ShadowUtils.Asint( v1 );
p2 = ShadowUtils.Asint( v2 );
p3 = ShadowUtils.Asint( v3 );
}
public void Set( Vector4 v ) { Set( v.x, v.y, v.z, v.w ); }
}
// Class holding resource information that needs to be synchronized with shaders.
public class ShadowContextStorage
{
public struct Init
{
public uint maxShadowDataSlots;
public uint maxPayloadSlots;
public uint maxTex2DArraySlots;
public uint maxTexCubeArraySlots;
public uint maxComparisonSamplerSlots;
public uint maxSamplerSlots;
}
protected ShadowContextStorage( ref Init initializer )
{
m_ShadowDatas.Reserve( initializer.maxShadowDataSlots );
m_Payloads.Reserve( initializer.maxPayloadSlots );
m_Tex2DArray.Reserve( initializer.maxTex2DArraySlots );
m_TexCubeArray.Reserve( initializer.maxTexCubeArraySlots );
m_CompSamplers.Reserve( initializer.maxComparisonSamplerSlots );
m_Samplers.Reserve( initializer.maxSamplerSlots );
}
// query functions to be used by the shadowmap
public uint RequestTex2DArraySlot() { return m_Tex2DArray.Add( new RenderTargetIdentifier() ); }
public uint RequestTexCubeArraySlot() { return m_TexCubeArray.Add( new RenderTargetIdentifier() ); }
public uint RequestSamplerSlot( SamplerState ss )
{
uint idx;
if( m_Samplers.FindFirst( out idx, ref ss ) )
return idx;
idx = m_Samplers.Count();
m_Samplers.Add( ss );
return idx;
}
public uint RequestSamplerSlot( ComparisonSamplerState css )
{
uint idx;
if( m_CompSamplers.FindFirst( out idx, ref css ) )
return idx;
idx = m_CompSamplers.Count();
m_CompSamplers.Add( css );
return idx;
}
// setters called each frame on the shadowmap
public void SetTex2DArraySlot( uint slot, RenderTargetIdentifier val ) { m_Tex2DArray[slot] = val; }
public void SetTexCubeArraySlot( uint slot, RenderTargetIdentifier val ) { m_TexCubeArray[slot] = val; }
protected VectorArray<ShadowData> m_ShadowDatas = new VectorArray<ShadowData>( 0, false );
protected VectorArray<ShadowPayload> m_Payloads = new VectorArray<ShadowPayload>( 0, false );
protected VectorArray<RenderTargetIdentifier> m_Tex2DArray = new VectorArray<RenderTargetIdentifier>( 0, true );
protected VectorArray<RenderTargetIdentifier> m_TexCubeArray = new VectorArray<RenderTargetIdentifier>( 0, true );
protected VectorArray<ComparisonSamplerState> m_CompSamplers = new VectorArray<ComparisonSamplerState>( 0, true );
protected VectorArray<SamplerState> m_Samplers = new VectorArray<SamplerState>( 0, true );
}
// Class providing hooks to do the actual synchronization
public class ShadowContext : ShadowContextStorage
{
public delegate void SyncDel( ShadowContext sc );
public delegate void BindDel( ShadowContext sc, CommandBuffer cb );
public struct CtxtInit
{
public Init storage;
public SyncDel dataSyncer;
public BindDel resourceBinder;
}
public ShadowContext( ref CtxtInit initializer ) : base( ref initializer.storage )
{
Debug.Assert( initializer.dataSyncer != null && initializer.resourceBinder != null );
m_DataSyncerDel = initializer.dataSyncer;
m_ResourceBinderDel = initializer.resourceBinder;
}
public void ClearData() { m_ShadowDatas.Reset(); m_Payloads.Reset(); }
// delegate that takes care of syncing data to the GPU
public void SyncData() { m_DataSyncerDel( this ); }
// delegate that takes care of binding textures, buffers and samplers to shaders just before rendering
public void BindResources( CommandBuffer cb ) { m_ResourceBinderDel( this, cb ); }
// the following functions are to be used by the bind and sync delegates
public void GetShadowDatas( out ShadowData[] shadowDatas, out uint offset, out uint count ) { shadowDatas = m_ShadowDatas.AsArray( out offset, out count ); }
public void GetPayloads( out ShadowPayload[] payloads, out uint offset, out uint count ) { payloads = m_Payloads.AsArray( out offset, out count ); }
public void GetTex2DArrays( out RenderTargetIdentifier[] tex2DArrays, out uint offset, out uint count ) { tex2DArrays = m_Tex2DArray.AsArray( out offset, out count ); }
public void GetTexCubeArrays( out RenderTargetIdentifier[] texCubeArrays, out uint offset, out uint count ) { texCubeArrays = m_TexCubeArray.AsArray( out offset, out count ); }
public void GetComparisonSamplerArrays( out ComparisonSamplerState[] compSamplers, out uint offset, out uint count ) { compSamplers = m_CompSamplers.AsArray( out offset, out count ); }
public void GetSamplerArrays( out SamplerState[] samplerArrays, out uint offset, out uint count ) { samplerArrays = m_Samplers.AsArray( out offset, out count ); }
private SyncDel m_DataSyncerDel;
private BindDel m_ResourceBinderDel;
}
// Abstract base class for handling shadow maps.
// Specific implementations managing atlases and the likes should inherit from this
abstract public class ShadowmapBase
{
[Flags]
public enum ShadowSupport
{
Point = 1 << GPUShadowType.Point,
Spot = 1 << GPUShadowType.Spot,
Directional = 1 << GPUShadowType.Directional
}
public struct ShadowRequest
{
private const byte k_IndexBits = 24;
private const byte k_FaceBits = 32 - k_IndexBits;
private const uint k_MaxIndex = (1 << k_IndexBits) - 1;
private const byte k_MaxFace = (1 << k_FaceBits) - 1;
public const int k_MaxFaceCount = k_FaceBits;
// combined face mask and visible light index
private uint m_MaskIndex;
// instance Id for this light
public int instanceId { get; set; }
// shadow type of this light
public GPUShadowType shadowType { get; set; }
// index into the visible lights array
public uint index
{
get { return m_MaskIndex & k_MaxIndex; }
set { m_MaskIndex = value & k_MaxIndex; }
}
// mask of which faces are requested:
// - for spotlights the value is always 1
// - for point lights the bit positions map to the faces as listed in the CubemapFace enum
// - for directional lights the bit positions map to the individual cascades
public uint facemask
{
get { return (m_MaskIndex >> k_IndexBits) & k_MaxFace; }
set { m_MaskIndex = (m_MaskIndex & k_MaxIndex) | (value << k_IndexBits); }
}
public uint facecount
{
get
{
uint fc = facemask;
uint count = 0;
while (fc != 0)
{
count += fc & 1;
fc >>= 1;
}
return count;
}
}
}
protected readonly uint m_Width;
protected readonly uint m_Height;
protected readonly uint m_Slices;
protected readonly uint m_ShadowmapBits;
protected readonly RenderTextureFormat m_ShadowmapFormat;
protected readonly SamplerState m_SamplerState;
protected readonly ComparisonSamplerState m_CompSamplerState;
protected readonly Vector4 m_ClearColor;
protected readonly float m_WidthRcp;
protected readonly float m_HeightRcp;
protected readonly uint m_MaxPayloadCount;
protected readonly ShadowSupport m_ShadowSupport;
protected uint m_ShadowId;
protected CullResults m_CullResults; // TODO: Temporary, due to CullResults dependency in ShadowUtils' matrix extraction code. Remove this member once that dependency is gone.
public struct BaseInit
{
public uint width; // width of the shadowmap
public uint height; // height of the shadowmap
public uint slices; // slices for the shadowmap
public uint shadowmapBits; // bit depth for native shadowmaps, or bitdepth for the temporary shadowmap if the shadowmapFormat is not native
public RenderTextureFormat shadowmapFormat; // texture format of the shadowmap
public SamplerState samplerState; // the desired sampler state for non-native shadowmaps
public ComparisonSamplerState comparisonSamplerState; // the desired sampler state for native shadowmaps doing depth comparisons as well
public Vector4 clearColor; // the clear color used for non-native shadowmaps
public uint maxPayloadCount; // how many ints will be pushed into the payload buffer for each invocation of Reserve
public ShadowSupport shadowSupport; // bitmask of all shadow types that this shadowmap supports
};
protected ShadowmapBase( ref BaseInit initializer )
{
m_Width = initializer.width;
m_Height = initializer.height;
m_Slices = initializer.slices;
m_ShadowmapBits = initializer.shadowmapBits;
m_ShadowmapFormat = initializer.shadowmapFormat;
m_SamplerState = initializer.samplerState;
m_CompSamplerState = initializer.comparisonSamplerState;
m_ClearColor = initializer.clearColor;
m_WidthRcp = 1.0f / initializer.width;
m_HeightRcp = 1.0f / initializer.height;
m_MaxPayloadCount = initializer.maxPayloadCount;
m_ShadowSupport = initializer.shadowSupport;
m_ShadowId = 0;
if( IsNativeDepth() && m_Slices > 1 )
{
// TODO: Right now when using any of the SetRendertarget functions we ultimately end up in RenderTextureD3D11.cpp
// SetRenderTargetD3D11Internal. This function sets the correct slice only for RTVs, whereas depth textures only
// support one DSV. So there's currently no way to have individual DSVs per slice to render into (ignoring going through a geometry shader and selecting the slice there).
Debug.LogError( "Unity does not allow direct rendering into specific depth slices, yet. Defaulting back to one array slice." );
m_Slices = 1;
}
}
protected bool IsNativeDepth()
{
return m_ShadowmapFormat == RenderTextureFormat.Shadowmap || m_ShadowmapFormat == RenderTextureFormat.Depth;
}
public ShadowSupport QueryShadowSupport() { return m_ShadowSupport; }
public uint GetMaxPayload() { return m_MaxPayloadCount; }
public void AssignId( uint shadowId ) { m_ShadowId = shadowId; }
public void Assign( CullResults cullResults ) { m_CullResults = cullResults; } // TODO: Remove when m_CullResults is removed again
abstract public bool Reserve( FrameId frameId, ref ShadowData shadowData, ShadowRequest sr, uint width, uint height, ref VectorArray<ShadowData> entries, ref VectorArray<ShadowPayload> payloads, VisibleLight[] lights );
abstract public bool Reserve( FrameId frameId, ref ShadowData shadowData, ShadowRequest sr, uint[] widths, uint[] heights, ref VectorArray<ShadowData> entries, ref VectorArray<ShadowPayload> payloads, VisibleLight[] lights );
abstract public bool ReserveFinalize( FrameId frameId, ref VectorArray<ShadowData> entries, ref VectorArray<ShadowPayload> payloads );
abstract public void Update( FrameId frameId, ScriptableRenderContext renderContext, CullResults cullResults, VisibleLight[] lights );
abstract public void ReserveSlots( ShadowContextStorage sc );
abstract public void Fill( ShadowContextStorage cs );
}
interface IShadowManager
{
// Warning: The shadowRequests array and shadowRequestsCount are modified by this function.
// When called the array contains the indices of lights requesting shadows,
// upon returning the array contains up to shadowRequestsCount valid shadow caster indices,
// whereas [shadowRequestsCount;originalRequestsCount) will hold all indices for lights that wanted to cast a shadow but got rejected.
// shadowDataIndices contains the offset into the shadowDatas array only for each shadow casting light, e.g. lights[shadowRequests[i]].shadowDataOffset = shadowDataIndices[i];
// shadowDatas contains shadowmap related basic parameters that can be passed to the shader.
// shadowPayloads contains implementation specific data that is accessed from the shader by indexing into an Buffer<int> using ShadowData.ShadowmapData.payloadOffset.
// This is the equivalent of a void pointer in the shader and there needs to be loader code that knows how to interpret the data.
// If there are no valid shadow casters all output arrays will be null, otherwise they will contain valid data that can be passed to shaders.
void ProcessShadowRequests( FrameId frameId, CullResults cullResults, Camera camera, VisibleLight[] lights, ref uint shadowRequestsCount, int[] shadowRequests, out int[] shadowDataIndices );
// Renders all shadows for lights the were deemed shadow casters after the last call to ProcessShadowRequests
void RenderShadows( FrameId frameId, ScriptableRenderContext renderContext, CullResults cullResults, VisibleLight[] lights );
// Synchronize data with GPU buffers
void SyncData();
// Binds resources to shader stages just before rendering the lighting pass
void BindResources( ScriptableRenderContext renderContext );
}
abstract public class ShadowManagerBase : 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 );
public abstract void SyncData();
public abstract void BindResources( ScriptableRenderContext renderContext );
// sort the shadow requests in descending priority - may only modify shadowRequests
protected abstract void PrioritizeShadowCasters( Camera camera, VisibleLight[] lights, uint shadowRequestsCount, int[] shadowRequests );
// prune the shadow requests - may modify shadowRequests and shadowsCountshadowRequestsCount
protected abstract void PruneShadowCasters( Camera camera, VisibleLight[] lights, ref VectorArray<int> shadowRequests, ref VectorArray<ShadowmapBase.ShadowRequest> requestsGranted, out uint totalRequestCount );
// allocate the shadow requests in the shadow map, only is called if shadowsCount > 0 - may modify shadowRequests and shadowsCount
protected abstract void AllocateShadows( FrameId frameId, VisibleLight[] lights, uint totalGranted, ref VectorArray<ShadowmapBase.ShadowRequest> grantedRequests, ref VectorArray<int> shadowIndices, ref VectorArray<ShadowData> shadowmapDatas, ref VectorArray<ShadowPayload> shadowmapPayload );
}
} // end of namespace ShadowExp
} // end of namespace UnityEngine.Experimental.ScriptableRenderLoop

71
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.
//
#ifndef SHADOWBASE_CS_HLSL
#define SHADOWBASE_CS_HLSL
//
// UnityEngine.Experimental.Rendering.HDPipeline.GPUShadowType: static fields
//
#define GPUSHADOWTYPE_POINT (0)
#define GPUSHADOWTYPE_SPOT (1)
#define GPUSHADOWTYPE_DIRECTIONAL (2)
#define GPUSHADOWTYPE_MAX (3)
// Generated from UnityEngine.Experimental.Rendering.HDPipeline.ShadowExp.ShadowData
// PackingRules = Exact
struct ShadowData
{
float4x4 worldToShadow;
float4 scaleOffset;
float2 texelSizeRcp;
uint id;
int shadowType;
uint payloadOffset;
int lightType;
float bias;
float quality;
};
//
// Accessors for UnityEngine.Experimental.Rendering.HDPipeline.ShadowExp.ShadowData
//
float4x4 GetWorldToShadow(ShadowData value)
{
return value.worldToShadow;
}
float4 GetScaleOffset(ShadowData value)
{
return value.scaleOffset;
}
float2 GetTexelSizeRcp(ShadowData value)
{
return value.texelSizeRcp;
}
uint GetId(ShadowData value)
{
return value.id;
}
int GetShadowType(ShadowData value)
{
return value.shadowType;
}
uint GetPayloadOffset(ShadowData value)
{
return value.payloadOffset;
}
int GetLightType(ShadowData value)
{
return value.lightType;
}
float GetBias(ShadowData value)
{
return value.bias;
}
float GetQuality(ShadowData value)
{
return value.quality;
}
#endif

9
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs.hlsl.meta


fileFormatVersion: 2
guid: 68d59da52ffb57240bdf73300a2736bf
timeCreated: 1485788693
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

12
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowBase.cs.meta


fileFormatVersion: 2
guid: 0998db889743f994b8ea4257f9ab33fa
timeCreated: 1485511431
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

29
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_TEXCUBEARRAY 1
#define SHADOWCONTEXT_MAX_SAMPLER 1
#define SHADOWCONTEXT_MAX_COMPSAMPLER 2
SHADOWCONTEXT_DECLARE( SHADOWCONTEXT_MAX_TEX2DARRAY, SHADOWCONTEXT_MAX_TEXCUBEARRAY, SHADOWCONTEXT_MAX_COMPSAMPLER, SHADOWCONTEXT_MAX_SAMPLER );
StructuredBuffer<ShadowData> _ShadowDatasExp;
StructuredBuffer<int4> _ShadowPayloads;
TEXTURE2D_ARRAY(_ShadowmapExp_Dir);
SAMPLER2D_SHADOW(sampler_ShadowmapExp_Dir);
TEXTURE2D_ARRAY(_ShadowmapExp_PointSpot);
SAMPLER2D_SHADOW(sampler_ShadowmapExp_PointSpot);
ShadowContext InitShadowContext()
{
ShadowContext sc;
sc.shadowDatas = _ShadowDatasExp;
sc.payloads = _ShadowPayloads;
sc.tex2DArray[0] = _ShadowmapExp_Dir;
sc.tex2DArray[1] = _ShadowmapExp_PointSpot;
sc.compSamplers[0] = sampler_ShadowmapExp_Dir;
sc.compSamplers[1] = sampler_ShadowmapExp_PointSpot;
return sc;
}

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


fileFormatVersion: 2
guid: 4091aef5166b0624db069f6dbfd0673a
timeCreated: 1485511901
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

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


// This file is empty by default.
// Project specific file to override the default shadow sampling routines.
// We need to define which dispatchers we're overriding, otherwise the compiler will pick default implementations which will lead to compile errors.
// Check Shadow.hlsl right below where this header is included for the individual defines.
// 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 )
{
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 );
}
float GetDirectionalShadowAttenuation( ShadowContext shadowContext, float3 positionWS, 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 );
}
#endif
// This is an example of how to override the default dynamic resource dispatcher
// by hardcoding the resources used and calling the shadow sampling routines that take an explicit texture and sampler.
// It is the responsibility of the author to make sure that ShadowContext.hlsl binds the correct texture to the right slot,
// 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 )
{
Texture2DArray tex = shadowContext.tex2DArray[1];
SamplerComparisonState compSamp = shadowContext.compSamplers[1];
return EvalShadow_PunctualDepth( shadowContext, tex, compSamp, positionWS, shadowDataIndex, L );
}
float GetPunctualShadowAttenuation( ShadowContext shadowContext, float3 positionWS, 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 );
}
#endif

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


fileFormatVersion: 2
guid: 1c09a112d6337c7468843f1d64d7795f
timeCreated: 1485511901
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

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


// Various shadow sampling logic.
// Again two versions, one for dynamic resource indexing, one for static resource access.
//
// 1 tap PCF sampling
//
float SampleShadow_PCF_1tap( ShadowContext shadowContext, float3 tcs, uint slice, uint texIdx, uint sampIdx )
{
// sample the texture
return SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, tcs, slice ).x;
}
float SampleShadow_PCF_1tap( ShadowContext shadowContext, float3 tcs, uint slice, Texture2DArray tex, SamplerComparisonState compSamp )
{
// sample the texture
return SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, tcs, slice );
}
//
// 9 tap adaptive PCF sampling
//
float SampleShadow_PCF_9tap_Adaptive( ShadowContext shadowContext, float2 texelSizeRcp, float3 tcs, uint slice, uint texIdx, uint sampIdx )
{
// 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 );
float4 vShadow3x3PCFTerms2 = float4( texelSizeRcp.x, texelSizeRcp.y, 0.0f, 0.0f );
float4 vShadow3x3PCFTerms3 = float4(-texelSizeRcp.x, -texelSizeRcp.y, 0.0f, 0.0f );
float4 v20Taps;
v20Taps.x = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms1.xy, tcs.z ), slice ).x; // 1 1
v20Taps.y = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms1.zy, tcs.z ), slice ).x; // -1 1
v20Taps.z = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms1.xw, tcs.z ), slice ).x; // 1 -1
v20Taps.w = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms1.zw, tcs.z ), slice ).x; // -1 -1
float flSum = dot( v20Taps.xyzw, float4( 0.25, 0.25, 0.25, 0.25 ) );
// fully in light or shadow? -> bail
if( ( flSum == 0.0 ) || ( flSum == 1.0 ) )
return flSum;
// we're in a transition area, do 5 more taps
flSum *= vShadow3x3PCFTerms0.x * 4.0;
float4 v33Taps;
v33Taps.x = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms2.xz, tcs.z ), slice ).x; // 1 0
v33Taps.y = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms3.xz, tcs.z ), slice ).x; // -1 0
v33Taps.z = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms3.zy, tcs.z ), slice ).x; // 0 -1
v33Taps.w = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, float3( tcs.xy + vShadow3x3PCFTerms2.zy, tcs.z ), slice ).x; // 0 1
flSum += dot( v33Taps.xyzw, vShadow3x3PCFTerms0.yyyy );
flSum += SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, tcs, slice ).x * vShadow3x3PCFTerms0.z;
return flSum;
}
float SampleShadow_PCF_9tap_Adaptive(ShadowContext shadowContext, float2 texelSizeRcp, float3 tcs, uint slice, Texture2DArray tex, SamplerComparisonState compSamp )
{
// 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);
float4 vShadow3x3PCFTerms2 = float4( texelSizeRcp.x, texelSizeRcp.y, 0.0f, 0.0f);
float4 vShadow3x3PCFTerms3 = float4(-texelSizeRcp.x, -texelSizeRcp.y, 0.0f, 0.0f);
float4 v20Taps;
v20Taps.x = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms1.xy, tcs.z ), slice ).x; // 1 1
v20Taps.y = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms1.zy, tcs.z ), slice ).x; // -1 1
v20Taps.z = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms1.xw, tcs.z ), slice ).x; // 1 -1
v20Taps.w = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms1.zw, tcs.z ), slice ).x; // -1 -1
float flSum = dot( v20Taps.xyzw, float4( 0.25, 0.25, 0.25, 0.25 ) );
// fully in light or shadow? -> bail
if( ( flSum == 0.0 ) || ( flSum == 1.0 ) )
return flSum;
// we're in a transition area, do 5 more taps
flSum *= vShadow3x3PCFTerms0.x * 4.0;
float4 v33Taps;
v33Taps.x = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms2.xz, tcs.z ), slice ).x; // 1 0
v33Taps.y = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms3.xz, tcs.z ), slice ).x; // -1 0
v33Taps.z = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms3.zy, tcs.z ), slice ).x; // 0 -1
v33Taps.w = SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, float3( tcs.xy + vShadow3x3PCFTerms2.zy, tcs.z ), slice ).x; // 0 1
flSum += dot( v33Taps.xyzw, vShadow3x3PCFTerms0.yyyy );
flSum += SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, compSamp, tcs, slice ).x * vShadow3x3PCFTerms0.z;
return flSum;
}

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


fileFormatVersion: 2
guid: 32c86a80860fd014ba44f8b7a7914a5c
timeCreated: 1487253249
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

81
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowTexFetch.hlsl


// shadow lookup routines when dynamic array access is possible
#if SHADOW_SUPPORTS_DYNAMIC_INDEXING != 0
// Shader model >= 5.1
# define SHADOW_DEFINE_SAMPLING_FUNCS( _Tex2DArraySlots, _TexCubeArraySlots ) \
float4 SampleCompShadow_T2DA( ShadowContext ctxt, uint texIdx, uint sampIdx, float3 tcs, float slice ) { return SAMPLE_TEXTURE2D_ARRAY_SHADOW( ctxt.tex2DArray[texIdx], ctxt.compSamplers[sampIdx], tcs, slice ); } \
float4 SampleShadow_T2DA( ShadowContext ctxt, uint texIdx, uint sampIdx, float2 tcs, float slice, float lod = 0.0 ) { return SAMPLE_TEXTURE2D_ARRAY_LOD( ctxt.tex2DArray[texIdx], ctxt.samplers[sampIdx], tcs, slice, lod ); } \
float4 SampleCompShadow_TCA( ShadowContext ctxt, uint texIdx, uint sampIdx, float4 tcs, float cubeIdx ) { return SAMPLE_TEXTURECUBE_ARRAY_SHADOW( ctxt.texCubeArray[texIdx], ctxt.compSamplers[sampIdx], tcs, cubeIdx );} \
float4 SampleShadow_TCA( ShadowContext ctxt, uint texIdx, uint sampIdx, float3 tcs, float cubeIdx, float lod = 0.0 ) { return SAMPLE_TEXTURECUBE_ARRAY_LOD( ctxt.texCubeArray[texIdx], ctxt.samplers[sampIdx], tcs, cubeIdx, lod ); }
#else // helper macros if dynamic indexing does not work
// Sampler and texture combinations are static. No shadowmap will ever be sampled with two different samplers.
// Once shadowmaps and samplers are fixed consider writing custom dispatchers directly accessing textures and samplers.
# define SHADOW_DEFINE_SAMPLING_FUNCS( _Tex2DArraySlots, _TexCubeArraySlots, _SamplerCompSlots, _SamplerSlots ) \
\
float4 SampleCompShadow_T2DA( ShadowContext ctxt, uint texIdx, uint sampIdx, float3 tcs, float slice ) \
{ \
[unroll] for( uint i = 0; i < _Tex2DArraySlots; i++ ) \
{ \
[unroll] for( uint j = 0; j < _SamplerCompSlots; j++ ) \
{ \
[branch] if( i == texIdx && j == sampIdx ) \
{ \
return SAMPLE_TEXTURE2D_ARRAY_SHADOW( ctxt.tex2DArray[i], ctxt.compSamplers[j], tcs, slice ); \
} \
} \
} \
return 1.0; \
} \
\
float4 SampleShadow_T2DA( ShadowContext ctxt, uint texIdx, uint sampIdx, float2 tcs, float slice, float lod = 0.0 ) \
{ \
[unroll] for( uint i = 0; i < _Tex2DArraySlots; i++ ) \
{ \
[unroll] for( uint j = 0; j < _SamplerSlots; j++ ) \
{ \
[branch] if( i == texIdx && j == sampIdx ) \
{ \
return SAMPLE_TEXTURE2D_ARRAY_LOD( ctxt.tex2DArray[i], ctxt.samplers[j], tcs, slice, lod ); \
} \
} \
} \
return 1.0; \
} \
\
float4 SampleCompShadow_TCA( ShadowContext ctxt, uint texIdx, uint sampIdx, float4 tcs, float cubeIdx ) \
{ \
[unroll] for( uint i = 0; i < _TexCubeArraySlots; i++ ) \
{ \
[unroll] for( uint j = 0; j < _SamplerCompSlots; j++ ) \
{ \
[branch] if( i == texIdx && j == sampIdx ) \
{ \
return SAMPLE_TEXTURECUBE_ARRAY_SHADOW( ctxt.texCubeArray[i], ctxt.compSamplers[j], tcs, cubeIdx ); \
} \
} \
} \
return 1.0; \
} \
\
float4 SampleShadow_TCA( ShadowContext ctxt, uint texIdx, uint sampIdx, float3 tcs, float cubeIdx, float lod = 0.0 ) \
{ \
[unroll] for( uint i = 0; i < _TexCubeArraySlots; i++ ) \
{ \
[unroll] for( uint j = 0; j < _SamplerSlots; j++ ) \
{ \
[branch] if( i == texIdx && j == sampIdx ) \
{ \
return SAMPLE_TEXTURECUBE_ARRAY_LOD( ctxt.texCubeArray[i], ctxt.samplers[j], tcs, cubeIdx, lod ); \
} \
} \
} \
return 1.0; \
}
#endif

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


fileFormatVersion: 2
guid: 2ac0d1cc115e6ec42b5c47d2ee13f139
timeCreated: 1485873843
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

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


namespace UnityEngine.Experimental.Rendering.HDPipeline
{
public class ShadowUtils
{
public static Matrix4x4 ExtractSpotLightMatrix( VisibleLight vl, out Matrix4x4 view, out Matrix4x4 proj, out Vector4 lightDir, out ShadowSplitData splitData )
{
splitData = new ShadowSplitData();
splitData.cullingSphere.Set( 0.0f, 0.0f, 0.0f, float.NegativeInfinity );
splitData.cullingPlaneCount = 0;
// get lightDir
lightDir = vl.light.transform.forward;
// calculate view
Matrix4x4 scaleMatrix = Matrix4x4.identity;
scaleMatrix.m22 = -1.0f;
view = scaleMatrix * vl.localToWorld.inverse;
// following code is from SharedLightData::GetNearPlaneMinBound
float percentageBound = 0.01f * vl.light.range;
float fixedBound = 0.1f;
float nearmin = fixedBound <= percentageBound ? fixedBound : percentageBound;
// calculate projection
float zfar = vl.range;
float znear = vl.light.shadowNearPlane >= nearmin ? vl.light.shadowNearPlane : nearmin;
float fov = vl.spotAngle;
proj = Matrix4x4.Perspective(fov, 1.0f, znear, zfar);
// and the compound
return proj * view;
}
public static Matrix4x4 ExtractPointLightMatrix( VisibleLight vl, uint faceIdx, float fovBias, out Matrix4x4 view, out Matrix4x4 proj, out Vector4 lightDir, out ShadowSplitData splitData, CullResults cullResults, int lightIndex )
{
Debug.Assert( faceIdx <= (uint) CubemapFace.NegativeZ, "Tried to extract cubemap face " + faceIdx + "." );
splitData = new ShadowSplitData();
splitData.cullingSphere.Set(0.0f, 0.0f, 0.0f, float.NegativeInfinity);
splitData.cullingPlaneCount = 0;
// get lightDir
lightDir = vl.light.transform.forward;
// TODO: At some point this logic should be moved to C#, then the parameters cullResults and lightIndex can be removed as well
cullResults.ComputePointShadowMatricesAndCullingPrimitives( lightIndex, (CubemapFace) faceIdx, fovBias, out view, out proj, out splitData );
// and the compound
return proj * view;
}
public static Matrix4x4 ExtractDirectionalLightMatrix( VisibleLight vl, uint cascadeIdx, int cascadeCount, Vector3 splitRatio, float nearPlaneOffset, uint width, uint height, out Matrix4x4 view, out Matrix4x4 proj, out Vector4 lightDir, out ShadowSplitData splitData, CullResults cullResults, int lightIndex )
{
Debug.Assert( width == height, "Currently the cascaded shadow mapping code requires square cascades." );
splitData = new ShadowSplitData();
splitData.cullingSphere.Set(0.0f, 0.0f, 0.0f, float.NegativeInfinity);
splitData.cullingPlaneCount = 0;
// get lightDir
lightDir = vl.light.transform.forward;
// TODO: At some point this logic should be moved to C#, then the parameters cullResults and lightIndex can be removed as well
// For directional lights shadow data is extracted from the cullResults, so that needs to be somehow provided here.
// Check ScriptableShadowsUtility.cpp ComputeDirectionalShadowMatricesAndCullingPrimitives(...) for details.
cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives( lightIndex, (int) cascadeIdx, cascadeCount, splitRatio, (int) width, nearPlaneOffset, out view, out proj, out splitData );
// and the compound
return proj * view;
}
public static bool MapLightType( LightArchetype la, LightType lt, out GPULightType gputype, out GPUShadowType shadowtype )
{
switch( la )
{
case LightArchetype.Punctual : return MapLightType( lt, out gputype, out shadowtype );
case LightArchetype.Rectangle : gputype = GPULightType.Rectangle; shadowtype = GPUShadowType.Unknown; return true;
case LightArchetype.Line : gputype = GPULightType.Line; shadowtype = GPUShadowType.Unknown; return true;
default : gputype = GPULightType.Spot; shadowtype = GPUShadowType.Unknown; return false; // <- probably not what you want
}
}
public static bool MapLightType( LightType lt, out GPULightType gputype, out GPUShadowType shadowtype )
{
switch( lt )
{
case LightType.Spot : gputype = GPULightType.Spot; shadowtype = GPUShadowType.Spot; return true;
case LightType.Directional : gputype = GPULightType.Directional; shadowtype = GPUShadowType.Directional; return true;
case LightType.Point : gputype = GPULightType.Point; shadowtype = GPUShadowType.Point; return true;
default:
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 float Asfloat( uint val ) { return System.BitConverter.ToSingle( System.BitConverter.GetBytes( val ), 0 ); }
public static int Asint( float val ) { return System.BitConverter.ToInt32( System.BitConverter.GetBytes( val ), 0 ); }
public static uint Asuint( float val ) { return System.BitConverter.ToUInt32( System.BitConverter.GetBytes( val ), 0 ); }
}
} // end of namespace UnityEngine.Experimental.ScriptableRenderLoop

12
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/ShadowUtilities.cs.meta


fileFormatVersion: 2
guid: 99dfd76ad655ff441aef9bb0015a1f05
timeCreated: 1485511433
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

303
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/VectorArray.cs


using System;
namespace UnityEngine.Experimental
{
public struct VectorArray<T>
{
public const uint k_InvalidIdx = uint.MaxValue;
private T[] m_array; // backing storage
private uint m_offset; // offset into backing storage
private uint m_count; // active slots, <= m_array.Length
private bool m_clearToDefault; // if true, freed up slots will be default initialized
public VectorArray( uint capacity, bool clearToDefault )
{
m_array = new T[capacity];
m_offset = 0;
m_count = capacity;
m_clearToDefault = clearToDefault;
}
public VectorArray( T[] array, uint offset, uint count, bool clearToDefault )
{
m_array = array;
m_offset = offset;
m_count = count;
m_clearToDefault = clearToDefault;
}
public VectorArray( ref VectorArray<T> vec, uint offset, uint count )
{
m_array = vec.m_array;
m_offset = vec.m_offset + offset;
m_count = count;
m_clearToDefault = vec.m_clearToDefault;
}
// Reset fill count, but keep storage
public void Reset()
{
if( m_clearToDefault )
{
for( uint i = 0; i < m_count; ++i )
this[i] = default(T);
}
m_count = 0;
}
// Reset fill count and reserve enough storage
public void Reset( uint capacity )
{
if( m_array.Length < (m_offset+capacity) )
{
m_array = new T[capacity];
m_offset = 0;
m_count = 0;
}
else
Reset();
}
// Reserve additional storage
public void Reserve(uint capacity)
{
if (m_offset + m_count + capacity <= m_array.Length)
return;
if (m_count == 0)
{
m_array = new T[capacity];
}
else
{
T[] tmp = new T[m_count + capacity];
Array.Copy(m_array, m_offset, tmp, 0, m_count);
m_array = tmp;
}
m_offset = 0;
}
// Resize array, either by cutting off at the end, or adding potentially default initialized slots
// Resize will modify the count member as well, whereas reserve won't.
public void Resize( uint size )
{
if( size > m_count )
{
uint residue = size - m_count;
Reserve( residue );
}
else if( m_clearToDefault )
{
for( uint i = size; i < m_count; ++i )
this[i] = default( T );
}
m_count = size;
}
public delegate void Cleanup( T obj );
// Same as Resize(), with an additional delegate to do any cleanup on the potentially freed slots
public void Resize( uint size, Cleanup cleanupDelegate )
{
for( uint i = size; i < m_count; ++i )
cleanupDelegate( this[i] );
Resize( size );
}
// Same as Reset(), with an additional delegate to do any cleanup on the potentially freed slots
public void Reset( Cleanup cleanupDelegate )
{
for( uint i = 0; i < m_count; ++i )
cleanupDelegate( this[i] );
Reset();
}
// Same as Reset( uint capacity ), with an additional delegate to do any cleanup on the potentially freed slots
public void Reset( uint capacity, Cleanup cleanupDelegate )
{
for( uint i = 0; i < m_count; ++i )
cleanupDelegate( this[i] );
Reset( capacity );
}
// Add obj and reallocate if necessary. Returns the index where the object was added.
public uint Add(T obj)
{
Reserve( 1 );
uint idx = m_count;
this[idx] = obj;
m_count++;
return idx;
}
// Add multiple objects and reallocate if necessary. Returns the index where the first object was added.
public uint Add( T[] objs, uint count )
{
Reserve( count );
return AddUnchecked( objs, count );
}
public uint Add( ref VectorArray<T> vec )
{
Reserve( vec.Count() );
return AddUnchecked( ref vec );
}
// Add an object without doing size checks. Make sure to call Reserve( capacity ) before using this.
public uint AddUnchecked(T obj)
{
uint idx = m_count;
this[idx] = obj;
m_count++;
return idx;
}
// Add multiple objects without doing size checks. Make sure to call Reserve( capacity ) before using this.
public uint AddUnchecked( T[] objs, uint count )
{
uint idx = m_count;
Array.Copy( objs, 0, m_array, m_offset + idx, count );
m_count += count;
return idx;
}
public uint AddUnchecked( ref VectorArray<T> vec )
{
uint cnt = vec.Count();
uint idx = m_count;
Array.Copy( vec.m_array, vec.m_offset, m_array, m_offset + idx, cnt );
m_count += cnt;
return idx;
}
// Purge count number of elements from the end of the array.
public void Purge( uint count )
{
Resize( count > m_count ? 0 : (m_count - count) );
}
// Same as Purge with an additional cleanup delegate.
public void Purge( uint count, Cleanup cleanupDelegate )
{
Resize( count > m_count ? 0 : (m_count - count), cleanupDelegate );
}
// Copies the active elements to the destination. destination.Length must be large enough to hold all values.
public void CopyTo(T[] destination, int destinationStart, out uint count)
{
count = m_count;
Array.Copy(m_array, m_offset, destination, destinationStart, count);
}
// Swaps two elements in the array doing bounds checks.
public void Swap( uint first, uint second )
{
if( first >= m_count || second >= m_count )
throw new System.ArgumentException( "Swap indices are out of range." );
SwapUnchecked( first, second );
}
// Swaps two elements without any checks.
public void SwapUnchecked( uint first, uint second )
{
T tmp = this[first];
this[first] = this[second];
this[second] = tmp;
}
// Extracts information from objects contained in the array and stores them in the destination array.
// Conversion is performed by the extractor delegate for each object.
// The destination array must be large enough to hold all values.
// The output parameter count will contain the number of actual objects written out to the destination array.
public delegate U Extractor<U>(T obj);
public void ExtractTo<U>( U[] destination, int destinationStart, out uint count, Extractor<U> extractorDelegate )
{
if (destination.Length < m_count)
throw new System.ArgumentException("Destination array is too small for source array.");
count = m_count;
for( uint i = 0; i < m_count; ++i )
destination[destinationStart+i] = extractorDelegate( this[i] );
}
public void ExtractTo<U>( ref VectorArray<U> destination, Extractor<U> extractorDelegate )
{
destination.Reserve( m_count );
for( uint i = 0; i < m_count; ++i )
destination.AddUnchecked( extractorDelegate( this[i] ) );
}
// Cast to array. The output parameter will contain the array offset to valid members and the number of active elements.
// Accessing array elements outside of this interval will lead to undefined behavior.
public T[] AsArray( out uint offset, out uint count )
{
offset = m_offset;
count = m_count;
return m_array;
}
// Array access. No bounds checking here, make sure you know what you're doing.
public T this[uint index]
{
get { return m_array[m_offset+index]; }
set { m_array[m_offset+index] = value; }
}
// Returns the number of active elements in the vector.
public uint Count()
{
return m_count;
}
// Returns the total capacity of accessible backing storage
public uint CapacityTotal()
{
return (uint) m_array.Length - m_offset;
}
// Return the number of slots that can still be added before reallocation of the backing storage becomes necessary.
public uint CapacityAvailable()
{
return (uint) m_array.Length - m_offset - m_count;
}
// Default sort the active array content.
public void Sort()
{
Debug.Assert( m_count <= int.MaxValue && m_offset <= int.MaxValue );
Array.Sort( m_array, (int) m_offset, (int)m_count );
}
// Returns true if the element matches the designator according to the comparator. idx will hold the index to the first matched object in the array.
public delegate bool Comparator<U>( ref U designator, ref T obj );
public bool FindFirst<U>( out uint idx, ref U designator, Comparator<U> compareDelegate )
{
for( idx = 0; idx < m_count; ++idx )
{
T obj = this[idx];
if( compareDelegate( ref designator, ref obj ) )
return true;
}
idx = k_InvalidIdx;
return false;
}
// Returns true if the element matches the designator using the types native compare method. idx will hold the index to the first matched object in the array.
public bool FindFirst( out uint idx, ref T designator )
{
for( idx = 0; idx < m_count; ++idx )
{
if( this[idx].Equals( designator ) )
return true;
}
idx = k_InvalidIdx;
return false;
}
// Returns a vector representing a subrange. Contents are shared, not duplicated.
public VectorArray<T> Subrange( uint offset, uint count )
{
return new VectorArray<T>( m_array, m_offset + offset, count, m_clearToDefault );
}
}
}

12
Assets/ScriptableRenderPipeline/HDRenderPipeline/Shadow/VectorArray.cs.meta


fileFormatVersion: 2
guid: f5c1ec8ec44960d4bbf72eafaf47d8fd
timeCreated: 1485511435
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存