Evgenii Golubev 8 年前
当前提交
0aace9e1
共有 31 个文件被更改,包括 2610 次插入460 次删除
  1. 2
      Assets/ScriptableRenderLoop/AdditionalLightData.cs
  2. 2
      Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.asset.meta
  3. 461
      Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.cs
  4. 81
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/LightDefinition.cs
  5. 170
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/LightDefinition.cs.hlsl
  6. 8
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/Resources/Deferred.shader
  7. 105
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.cs
  8. 15
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.hlsl
  9. 11
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePassLoop.hlsl
  10. 59
      Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/Lit.hlsl
  11. 1
      Assets/ScriptableRenderLoop/HDRenderLoop/Material/Material.hlsl
  12. 25
      Assets/ScriptableRenderLoop/ShaderLibrary/Common.hlsl
  13. 22
      Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl
  14. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass.meta
  15. 12
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs.meta
  16. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources.meta
  17. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/lightlistbuild.compute.meta
  18. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/scrbound.compute.meta
  19. 442
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/lightlistbuild.compute
  20. 465
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/scrbound.compute
  21. 128
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/LightingConvexHullUtils.hlsl
  22. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/LightingConvexHullUtils.hlsl.meta
  23. 117
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/SortingComputeUtils.hlsl
  24. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/SortingComputeUtils.hlsl.meta
  25. 148
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs.hlsl
  26. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs.hlsl.meta
  27. 674
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs
  28. 50
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.cs.hlsl
  29. 9
      Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.cs.hlsl.meta

2
Assets/ScriptableRenderLoop/AdditionalLightData.cs


}
[RangeAttribute(0.0F, 1.0F)]
public float shadowDimmer = 1.0F;
public float shadowDimmer = 1.0f;
public bool affectDiffuse = true;
public bool affectSpecular = true;

2
Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.asset.meta


fileFormatVersion: 2
guid: 2400b74f5ce370c4481e5dc417d03703
timeCreated: 1479240578
timeCreated: 1479345431
licenseType: Pro
NativeFormatImporter:
userData:

461
Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.cs


{
[ExecuteInEditMode]
// This HDRenderLoop assume linear lighting. Don't work with gamma.
public class HDRenderLoop : ScriptableRenderLoop
public partial class HDRenderLoop : ScriptableRenderLoop
private const string k_HDRenderLoopPath = "Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.asset";
const string k_HDRenderLoopPath = "Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.asset";
#if UNITY_EDITOR
[UnityEditor.MenuItem("Renderloop/CreateHDRenderLoop")]
static void CreateHDRenderLoop()
{
var instance = ScriptableObject.CreateInstance<HDRenderLoop>();
UnityEditor.AssetDatabase.CreateAsset(instance, k_HDRenderLoopPath);
}
#endif
public class SkyParameters
{

}
[SerializeField]
private SkyParameters m_SkyParameters = new SkyParameters();
SkyParameters m_SkyParameters = new SkyParameters();
public SkyParameters skyParameters
{

public float exposure = 0;
}
private DebugParameters m_DebugParameters = new DebugParameters();
DebugParameters m_DebugParameters = new DebugParameters();
#if UNITY_EDITOR
[UnityEditor.MenuItem("Renderloop/CreateHDRenderLoop")]
static void CreateHDRenderLoop()
{
var instance = ScriptableObject.CreateInstance<HDRenderLoop>();
UnityEditor.AssetDatabase.CreateAsset(instance, k_HDRenderLoopPath);
}
#endif
public class GBufferManager
{
public const int MaxGbuffer = 8;

colorMRTs[index] = RTIDs[index];
}
/*
public void BindBuffers(Material mat)
{

}
*/
public int gbufferCount { get; set; }
int[] IDs = new int[MaxGbuffer];
RenderTargetIdentifier[] RTIDs = new RenderTargetIdentifier[MaxGbuffer];

public const int MaxLights = 32;
public const int MaxShadows = 16; // Max shadow allowed on screen simultaneously - a point light is 6 shadows
public const int MaxProbes = 32;
GBufferManager m_gbufferManager = new GBufferManager();
public const int k_MaxDirectionalLightsOnSCreen = 2;
public const int k_MaxPunctualLightsOnSCreen = 512;
public const int k_MaxAreaLightsOnSCreen = 128;
public const int k_MaxEnvLightsOnSCreen = 64;
public const int k_MaxShadowOnScreen = 16;
// Various set of material use in render loop
Material m_DebugViewMaterialGBuffer;
// TODO: Find a way to automatically create/iterate through deferred material
Lit.RenderLoop m_LitRenderLoop;
// Various buffer
int s_CameraColorBuffer;
int s_CameraDepthBuffer;
int s_VelocityBuffer;
int s_DistortionBuffer;
// Debug
Material m_DebugViewMaterialGBuffer;
public class LightList
{
public List<DirectionalLightData> directionalLights;
public List<LightData> punctualLights;
public List<LightData> areaLights;
public List<EnvLightData> envLights;
public List<PunctualShadowData> punctualShadows;
}
GBufferManager m_gbufferManager = new GBufferManager();
// Detect when windows size is changing
int m_WidthOnRecord;
int m_HeightOnRecord;
private int s_CameraColorBuffer;
private int s_CameraDepthBuffer;
private int s_VelocityBuffer;
private int s_DistortionBuffer;
// TODO: Find a way to automatically create/iterate through lightloop
SinglePass.LightLoop m_SinglePassLightLoop;
// TilePass.LightLoop m_TilePassLightLoop;
private ComputeBuffer s_punctualLightList;
private ComputeBuffer s_envLightList;
private ComputeBuffer s_areaLightList;
private ComputeBuffer s_punctualShadowList;
// TODO: Find a way to automatically create/iterate through deferred material
Lit.RenderLoop m_LitRenderLoop;
private TextureCacheCubemap m_cubeReflTexArray;
TextureCacheCubemap m_CubeReflTexArray;
TextureCache2D m_CookieTexArray;
TextureCacheCubemap m_CubeCookieTexArray;
void OnEnable()
{

Rebuild();
}
void ClearComputeBuffers()
{
if (s_punctualLightList != null)
s_punctualLightList.Release();
if (s_areaLightList != null)
s_areaLightList.Release();
if (s_punctualShadowList != null)
s_punctualShadowList.Release();
if (s_envLightList != null)
s_envLightList.Release();
}
Material CreateEngineMaterial(string shaderPath)
{
var mat = new Material(Shader.Find(shaderPath) as Shader)

public override void Rebuild()
{
ClearComputeBuffers();
s_punctualLightList = new ComputeBuffer(MaxLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(PunctualLightData)));
s_areaLightList = new ComputeBuffer(MaxLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(AreaLightData)));
s_envLightList = new ComputeBuffer(MaxLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(EnvLightData)));
s_punctualShadowList = new ComputeBuffer(MaxShadows, System.Runtime.InteropServices.Marshal.SizeOf(typeof(PunctualShadowData)));
// TODO: We need to have an API to send our sky information to Enlighten. For now use a workaround through skybox/cubemap material...
m_SkyboxMaterial = CreateEngineMaterial("Skybox/Cubemap");
RenderSettings.skybox = m_SkyboxMaterial; // Setup this material as the default to be use in RenderSettings

m_SkyHDRIMaterial = CreateEngineMaterial("Hidden/HDRenderLoop/SkyHDRI");
m_DeferredMaterial = CreateEngineMaterial("Hidden/HDRenderLoop/Deferred");
m_FinalPassMaterial = CreateEngineMaterial("Hidden/HDRenderLoop/FinalPass");
// Debug
m_ShadowPass = new ShadowRenderPass (m_ShadowSettings);
m_cubeReflTexArray = new TextureCacheCubemap();
m_cubeReflTexArray.AllocTextureArray(32, (int)m_TextureSettings.reflectionCubemapSize, TextureFormat.BC6H, true);
m_ShadowPass = new ShadowRenderPass(m_ShadowSettings);
m_LitRenderLoop = new Lit.RenderLoop(); // Our object can be garbacge collected, so need to be allocate here
m_LitRenderLoop = new Lit.RenderLoop(); // Our object can be garbage collected, so need to be allocate here
m_gbufferManager.gbufferCount = m_LitRenderLoop.GetMaterialGBufferCount();
RenderTextureFormat[] RTFormat; RenderTextureReadWrite[] RTReadWrite;

s_DistortionBuffer = Shader.PropertyToID("_DistortionTexture");
m_LitRenderLoop.Rebuild();
m_CookieTexArray = new TextureCache2D();
m_CookieTexArray.AllocTextureArray(8, (int)m_TextureSettings.spotCookieSize, (int)m_TextureSettings.spotCookieSize, TextureFormat.RGBA32, true);
m_CubeCookieTexArray = new TextureCacheCubemap();
m_CubeCookieTexArray.AllocTextureArray(4, (int)m_TextureSettings.pointCookieSize, TextureFormat.RGBA32, true);
m_CubeReflTexArray = new TextureCacheCubemap();
m_CubeReflTexArray.AllocTextureArray(32, (int)m_TextureSettings.reflectionCubemapSize, TextureFormat.BC6H, true);
// Init various light loop
m_SinglePassLightLoop = new SinglePass.LightLoop();
m_SinglePassLightLoop.Rebuild();
// m_TilePassLightLoop = new TilePass.LightLoop();
// m_TilePassLightLoop.Rebuild();
m_SinglePassLightLoop.OnDisable();
//m_TilePassLightLoop.OnDisable();
s_punctualLightList.Release();
s_areaLightList.Release();
s_envLightList.Release();
s_punctualShadowList.Release();
if (m_DebugViewMaterialGBuffer) DestroyImmediate(m_DebugViewMaterialGBuffer);
m_cubeReflTexArray.Release();
m_CubeReflTexArray.Release();
m_CookieTexArray.Release();
m_CubeCookieTexArray.Release();
}
void NewFrame()
{
m_CookieTexArray.NewFrame();
m_CubeCookieTexArray.NewFrame();
m_CubeReflTexArray.NewFrame();
}
void InitAndClearBuffer(Camera camera, RenderLoop renderLoop)

cmd.Dispose();
}
void NewFrame()
{
// update texture caches
m_cubeReflTexArray.NewFrame();
}
//---------------------------------------------------------------------------------------------------------------------------------------------------
void UpdatePunctualLights(VisibleLight[] visibleLights, ref ShadowOutput shadowOutput)
// Function to prepare light structure for GPU lighting
void ConvertLightForGPU(CullResults cullResults, ref ShadowOutput shadowOutput, out LightList lightList)
var pLights = new List<PunctualLightData>();
var aLights = new List<AreaLightData>();
var shadows = new List<PunctualShadowData>();
// Init light list
lightList = new LightList();
lightList.directionalLights = new List<DirectionalLightData>();
lightList.punctualLights = new List<LightData>();
lightList.areaLights = new List<LightData>();
lightList.envLights = new List<EnvLightData>();
lightList.punctualShadows = new List<PunctualShadowData>();
for (int lightIndex = 0, numLights = Math.Min(visibleLights.Length, MaxLights); lightIndex < numLights; ++lightIndex)
for (int lightIndex = 0, numLights = cullResults.visibleLights.Length; lightIndex < numLights; ++lightIndex)
var light = visibleLights[lightIndex];
var light = cullResults.visibleLights[lightIndex];
// We only process light with additional data
var additionalData = light.light.GetComponent<AdditionalLightData>();

continue;
}
if (light.lightType == LightType.Area)
// Linear intensity calculation (different Unity 5.5)
var lightColorR = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.r);
var lightColorG = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.g);
var lightColorB = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.b);
if (light.lightType == LightType.Directional)
// Skip area lights which are currently only used for baking.
if (lightList.areaLights.Count >= k_MaxDirectionalLightsOnSCreen)
continue;
var directionalLightData = new DirectionalLightData();
// Light direction for directional and is opposite to the forward direction
directionalLightData.direction = -light.light.transform.forward;
directionalLightData.color = new Vector3(lightColorR, lightColorG, lightColorB);
directionalLightData.diffuseScale = additionalData.affectDiffuse ? 1.0f : 0.0f;
directionalLightData.specularScale = additionalData.affectSpecular ? 1.0f : 0.0f;
directionalLightData.cosAngle = 0.0f;
directionalLightData.sinAngle = 0.0f;
directionalLightData.shadowIndex = -1;
// TODO: shadow
lightList.directionalLights.Add(directionalLightData);
// Correct intensity calculation (different from Unity)
var lightColorR = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.r);
var lightColorG = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.g);
var lightColorB = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.b);
// Note: LightType.Area is offline only, use for baking, no need to test it
var lightData = new LightData();
// Early out if we reach the maximum
AreaLightData lightData = new AreaLightData();
if (lightList.areaLights.Count >= k_MaxAreaLightsOnSCreen)
continue;
// TODO: add AreaShapeType.Line support for small widths.
lightData.shapeType = AreaShapeType.Rectangle;
lightData.size = new Vector2(additionalData.areaLightLength, additionalData.areaLightWidth);
lightData.twoSided = additionalData.isDoubleSided;
lightData.lightType = GPULightType.Rectangle;
}
else
{
if (lightList.punctualLights.Count >= k_MaxPunctualLightsOnSCreen)
continue;
lightData.positionWS = light.light.transform.position;
lightData.forward = light.light.transform.forward; // Note: Light direction is oriented backward (-Z)
lightData.up = light.light.transform.up;
lightData.right = light.light.transform.right;
switch (light.lightType)
{
case LightType.Directional: lightData.lightType = GPULightType.Directional; break;
case LightType.Spot: lightData.lightType = GPULightType.Spot; break;
case LightType.Point: lightData.lightType = GPULightType.Point; break;
}
}
lightData.color = new Vector3(lightColorR, lightColorG, lightColorB);
lightData.diffuseScale = additionalData.affectDiffuse ? 1.0f : 0.0f;
lightData.specularScale = additionalData.affectSpecular ? 1.0f : 0.0f;
lightData.shadowDimmer = additionalData.shadowDimmer;
lightData.positionWS = light.light.transform.position;
lightData.invSqrAttenuationRadius = 1.0f / (light.range * light.range);
lightData.color = new Vector3(lightColorR, lightColorG, lightColorB);
lightData.forward = light.light.transform.forward; // Note: Light direction is oriented backward (-Z)
lightData.up = light.light.transform.up;
lightData.right = light.light.transform.right;
lightData.invSqrAttenuationRadius = 1.0f / (light.range * light.range);
if (lightData.lightType == GPULightType.Spot)
{
var spotAngle = light.spotAngle;
aLights.Add(lightData);
var innerConePercent = additionalData.GetInnerSpotPercent01();
var cosSpotOuterHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * Mathf.Deg2Rad), 0.0f, 1.0f);
var cosSpotInnerHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * innerConePercent * Mathf.Deg2Rad), 0.0f, 1.0f); // inner cone
// TODO: shadows.
var val = Mathf.Max(0.001f, (cosSpotInnerHalfAngle - cosSpotOuterHalfAngle));
lightData.angleScale = 1.0f / val;
lightData.angleOffset = -cosSpotOuterHalfAngle * lightData.angleScale;
var l = new PunctualLightData();
if (light.lightType == LightType.Directional)
{
l.useDistanceAttenuation = 0.0f;
// positionWS store Light direction for directional and is opposite to the forward direction
l.positionWS = -light.light.transform.forward;
l.invSqrAttenuationRadius = 0.0f;
}
else
{
l.useDistanceAttenuation = 1.0f;
l.positionWS = light.light.transform.position;
l.invSqrAttenuationRadius = 1.0f / (light.range * light.range);
}
// 1.0f, 2.0f are neutral value allowing GetAngleAnttenuation in shader code to return 1.0
lightData.angleScale = 1.0f;
lightData.angleOffset = 2.0f;
}
l.color = new Vector3(lightColorR, lightColorG, lightColorB);
lightData.diffuseScale = additionalData.affectDiffuse ? 1.0f : 0.0f;
lightData.specularScale = additionalData.affectSpecular ? 1.0f : 0.0f;
lightData.shadowDimmer = additionalData.shadowDimmer;
l.forward = light.light.transform.forward; // Note: Light direction is oriented backward (-Z)
l.up = light.light.transform.up;
l.right = light.light.transform.right;
lightData.IESIndex = -1;
lightData.cookieIndex = -1;
lightData.shadowIndex = -1;
if (light.lightType == LightType.Spot)
bool hasCookie = light.light.cookie != null;
if (hasCookie)
{
if (light.lightType == LightType.Point)
var spotAngle = light.spotAngle;
var innerConePercent = additionalData.GetInnerSpotPercent01();
var cosSpotOuterHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * Mathf.Deg2Rad), 0.0f, 1.0f);
var cosSpotInnerHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * innerConePercent * Mathf.Deg2Rad), 0.0f, 1.0f); // inner cone
var val = Mathf.Max(0.001f, (cosSpotInnerHalfAngle - cosSpotOuterHalfAngle));
l.angleScale = 1.0f / val;
l.angleOffset = -cosSpotOuterHalfAngle * l.angleScale;
lightData.cookieIndex = m_CubeCookieTexArray.FetchSlice(light.light.cookie);
else
else if (light.lightType == LightType.Spot)
// 1.0f, 2.0f are neutral value allowing GetAngleAnttenuation in shader code to return 1.0
l.angleScale = 1.0f;
l.angleOffset = 2.0f;
lightData.cookieIndex = m_CookieTexArray.FetchSlice(light.light.cookie);
}
l.diffuseScale = additionalData.affectDiffuse ? 1.0f : 0.0f;
l.specularScale = additionalData.affectSpecular ? 1.0f : 0.0f;
l.shadowDimmer = additionalData.shadowDimmer;
// Setup shadow data arrays
bool hasShadows = light.light.shadows != LightShadows.None && shadowOutput.GetShadowSliceCountLightIndex(lightIndex) != 0;
bool hasNotReachMaxLimit = lightList.punctualShadows.Count + (lightData.lightType == GPULightType.Point ? 6 : 1) <= k_MaxShadowOnScreen;
l.IESIndex = -1;
l.cookieIndex = -1;
l.shadowIndex = -1;
if (hasShadows && hasNotReachMaxLimit) // Note < MaxShadows should be check at shadowOutput creation
{
// When we have a point light, we assumed that there is 6 consecutive PunctualShadowData
lightData.shadowIndex = lightList.punctualShadows.Count;
// Setup shadow data arrays
bool hasShadows = shadowOutput.GetShadowSliceCountLightIndex(lightIndex) != 0;
bool hasNotReachMaxLimit = shadows.Count + (light.lightType == LightType.Point ? 6 : 1) <= MaxShadows;
if (hasShadows && hasNotReachMaxLimit) // Note < MaxShadows should be check at shadowOutput creation
for (int sliceIndex = 0; sliceIndex < shadowOutput.GetShadowSliceCountLightIndex(lightIndex); ++sliceIndex)
// When we have a point light, we assumed that there is 6 consecutive PunctualShadowData
l.shadowIndex = shadows.Count;
for (int sliceIndex = 0; sliceIndex < shadowOutput.GetShadowSliceCountLightIndex(lightIndex); ++sliceIndex)
{
PunctualShadowData s = new PunctualShadowData();
PunctualShadowData punctualShadowData = new PunctualShadowData();
int shadowSliceIndex = shadowOutput.GetShadowSliceIndex(lightIndex, sliceIndex);
s.worldToShadow = shadowOutput.shadowSlices[shadowSliceIndex].shadowTransform.transpose; // Transpose for hlsl reading ?
if (light.lightType == LightType.Spot)
{
s.shadowType = ShadowType.Spot;
}
else if (light.lightType == LightType.Point)
{
s.shadowType = ShadowType.Point;
}
else
{
s.shadowType = ShadowType.Directional;
}
int shadowSliceIndex = shadowOutput.GetShadowSliceIndex(lightIndex, sliceIndex);
punctualShadowData.worldToShadow = shadowOutput.shadowSlices[shadowSliceIndex].shadowTransform.transpose; // Transpose for hlsl reading ?
punctualShadowData.lightType = lightData.lightType;
s.bias = light.light.shadowBias;
punctualShadowData.bias = light.light.shadowBias;
shadows.Add(s);
}
lightList.punctualShadows.Add(punctualShadowData);
pLights.Add(l);
}
s_punctualLightList.SetData(pLights.ToArray());
s_areaLightList.SetData(aLights.ToArray());
s_punctualShadowList.SetData(shadows.ToArray());
lightData.size = new Vector2(additionalData.areaLightLength, additionalData.areaLightWidth);
lightData.twoSided = additionalData.isDoubleSided;
Shader.SetGlobalBuffer("_PunctualLightList", s_punctualLightList);
Shader.SetGlobalBuffer("_AreaLightList", s_areaLightList);
Shader.SetGlobalInt("_PunctualLightCount", pLights.Count);
Shader.SetGlobalInt("_AreaLightCount", aLights.Count);
Shader.SetGlobalBuffer("_PunctualShadowList", s_punctualShadowList);
}
void UpdateReflectionProbes(VisibleReflectionProbe[] activeReflectionProbes)
{
var lights = new List<EnvLightData>();
if (additionalData.treatAsAreaLight)
{
lightList.areaLights.Add(lightData);
}
else
{
lightList.punctualLights.Add(lightData);
}
}
for (int lightIndex = 0; lightIndex < Math.Min(activeReflectionProbes.Length, MaxProbes); lightIndex++)
for (int probeIndex = 0, numProbes = cullResults.visibleReflectionProbes.Length; probeIndex < numProbes; probeIndex++)
var probe = activeReflectionProbes[lightIndex];
var probe = cullResults.visibleReflectionProbes[probeIndex];
// If probe have not been rendered discard
var l = new EnvLightData();
if (lightList.envLights.Count >= k_MaxEnvLightsOnSCreen)
continue;
var envLightData = new EnvLightData();
l.positionWS = probe.localToWorld.GetColumn(3);
envLightData.positionWS = probe.localToWorld.GetColumn(3);
l.envShapeType = EnvShapeType.None;
envLightData.envShapeType = EnvShapeType.None;
l.envShapeType = EnvShapeType.Box;
envLightData.envShapeType = EnvShapeType.Box;
l.right = probe.localToWorld.GetColumn(0);
l.right.Normalize();
l.up = probe.localToWorld.GetColumn(1);
l.up.Normalize();
l.forward = probe.localToWorld.GetColumn(2);
l.forward.Normalize();
envLightData.right = probe.localToWorld.GetColumn(0);
envLightData.right.Normalize();
envLightData.up = probe.localToWorld.GetColumn(1);
envLightData.up.Normalize();
envLightData.forward = probe.localToWorld.GetColumn(2);
envLightData.forward.Normalize();
// Artists prefer to have blend distance inside the volume!
// So we let the current UI but we assume blendDistance is an inside factor instead

float blendDistance = Mathf.Min(maxBlendDist, probe.blendDistance);
l.innerDistance = probe.bounds.extents - new Vector3(blendDistance, blendDistance, blendDistance);
envLightData.innerDistance = probe.bounds.extents - new Vector3(blendDistance, blendDistance, blendDistance);
l.envIndex = m_cubeReflTexArray.FetchSlice(probe.texture);
envLightData.envIndex = m_CubeReflTexArray.FetchSlice(probe.texture);
l.offsetLS = probe.center; // center is misnamed, it is the offset (in local space) from center of the bounding box to the cubemap capture point
l.blendDistance = blendDistance;
lights.Add(l);
envLightData.offsetLS = probe.center; // center is misnamed, it is the offset (in local space) from center of the bounding box to the cubemap capture point
envLightData.blendDistance = blendDistance;
lightList.envLights.Add(envLightData);
}
s_envLightList.SetData(lights.ToArray());
void Resize(Camera camera)
{ /*
if (camera.pixelWidth != m_WidthOnRecord || camera.pixelHeight != m_HeightOnRecord || m_TilePassLightLoop.NeedResize())
{
if (m_WidthOnRecord > 0 && m_HeightOnRecord > 0)
{
m_TilePassLightLoop.ReleaseResolutionDependentBuffers();
}
m_TilePassLightLoop.AllocResolutionDependentBuffers(camera.pixelWidth, camera.pixelHeight);
// update recorded window resolution
m_WidthOnRecord = camera.pixelWidth;
m_HeightOnRecord = camera.pixelHeight;
}
*/
}
public void PushGlobalParams(Camera camera, RenderLoop renderLoop, HDRenderLoop.LightList lightList)
{
//Shader.SetGlobalTexture("_CookieTextures", m_CookieTexArray.GetTexCache());
//Shader.SetGlobalTexture("_CubeCookieTextures", m_CubeCookieTexArray.GetTexCache());
Shader.SetGlobalTexture("_EnvTextures", m_CubeReflTexArray.GetTexCache());
Shader.SetGlobalBuffer("_EnvLightList", s_envLightList);
Shader.SetGlobalInt("_EnvLightCount", lights.Count);
Shader.SetGlobalTexture("_EnvTextures", m_cubeReflTexArray.GetTexCache());
m_SinglePassLightLoop.PushGlobalParams(camera, renderLoop, lightList);
// m_TilePassLightLoop.PushGlobalParams(camera, renderLoop, lightList);
}
public override void Render(Camera[] cameras, RenderLoop renderLoop)

if (!CullResults.GetCullingParameters(camera, out cullingParams))
continue;
m_ShadowPass.UpdateCullingParameters (ref cullingParams);
m_ShadowPass.UpdateCullingParameters(ref cullingParams);
Resize(camera);
renderLoop.SetupCameraProperties(camera);

RenderGBuffer(cullResults, camera, renderLoop);
RenderGBuffer(cullResults, camera, renderLoop);
// For tile lighting with forward opaque
//RenderForwardOpaqueDepth(cullResults, camera, renderLoop);

renderLoop.SetupCameraProperties(camera); // Need to recall SetupCameraProperties after m_ShadowPass.Render
UpdatePunctualLights(cullResults.visibleLights, ref shadows);
UpdateReflectionProbes(cullResults.visibleReflectionProbes);
LightList lightList;
ConvertLightForGPU(cullResults, ref shadows, out lightList);
PushGlobalParams(camera, renderLoop, lightList);
// build per tile light lists
//var numLights = 0; // GenerateSourceLightBuffers(camera, cullResults);
//m_tilePassLightLoop.BuildPerTileLightLists(camera, loop, numLights, projscr, invProjscr);
RenderDeferredLighting(camera, renderLoop);

81
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/LightDefinition.cs


// structure definition
//-----------------------------------------------------------------------------
[GenerateHLSL]
public enum GPULightType
{
Directional,
Spot,
Point,
ProjectorOrtho,
ProjectorPyramid,
// AreaLight
Rectangle,
Line,
// Currently not supported in real time (just use for reference)
Sphere,
Disk,
Hemisphere,
Cylinder
};
public struct PunctualLightData
public struct LightData
public float useDistanceAttenuation;
public float angleScale; // Spot light
public float angleScale;
public float angleOffset;
public float angleOffset;
public float diffuseScale; // Spot light
public float diffuseScale;
public Vector3 unused;
};
[GenerateHLSL]
public enum AreaShapeType
{
Rectangle,
Line,
// Currently not supported in real time (just use for reference)
Sphere,
Disk,
Hemisphere,
Cylinder
public GPULightType lightType;
// Area Light specific
public Vector2 size;
public bool twoSided;
public struct AreaLightData
public struct DirectionalLightData
public Vector3 positionWS;
public float invSqrAttenuationRadius;
public Vector3 direction;
public float diffuseScale;
public AreaShapeType shapeType;
public float specularScale;
public Vector3 forward;
public float diffuseScale;
// Sun disc size
public float cosAngle; // Distance to disk
public float sinAngle; // Disk radius
public int shadowIndex;
public float unsued;
};
public Vector3 up;
public float specularScale;
public Vector3 right;
public float shadowDimmer;
// TODO: we may have to add various parameters here for shadow - was suppose to be coupled with a light loop
// A point light is 6x PunctualShadowData
[GenerateHLSL]
public struct PunctualShadowData
{
// World to ShadowMap matrix
// Include scale and bias for shadow atlas if any
public Matrix4x4 worldToShadow;
public Vector2 size;
public bool twoSided;
public GPULightType lightType;
public float bias;
public float quality;
public float unused;
};

public Vector3 offsetLS;
public float unused2;
};
} // namespace UnityEngine.Experimental.ScriptableRenderLoop

170
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/LightDefinition.cs.hlsl


#ifndef LIGHTDEFINITION_CS_HLSL
#define LIGHTDEFINITION_CS_HLSL
//
// UnityEngine.Experimental.ScriptableRenderLoop.AreaShapeType: static fields
// UnityEngine.Experimental.ScriptableRenderLoop.GPULightType: static fields
#define AREASHAPETYPE_RECTANGLE (0)
#define AREASHAPETYPE_LINE (1)
#define AREASHAPETYPE_SPHERE (2)
#define AREASHAPETYPE_DISK (3)
#define AREASHAPETYPE_HEMISPHERE (4)
#define AREASHAPETYPE_CYLINDER (5)
#define GPULIGHTTYPE_DIRECTIONAL (0)
#define GPULIGHTTYPE_SPOT (1)
#define GPULIGHTTYPE_POINT (2)
#define GPULIGHTTYPE_PROJECTOR_ORTHO (3)
#define GPULIGHTTYPE_PROJECTOR_PYRAMID (4)
#define GPULIGHTTYPE_RECTANGLE (5)
#define GPULIGHTTYPE_LINE (6)
#define GPULIGHTTYPE_SPHERE (7)
#define GPULIGHTTYPE_DISK (8)
#define GPULIGHTTYPE_HEMISPHERE (9)
#define GPULIGHTTYPE_CYLINDER (10)
//
// UnityEngine.Experimental.ScriptableRenderLoop.EnvShapeType: static fields

#define ENVSHAPETYPE_SPHERE (2)
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.PunctualLightData
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.LightData
struct PunctualLightData
struct LightData
float useDistanceAttenuation;
float3 forward;
float3 forward;
float angleOffset;
float angleOffset;
float diffuseScale;
float diffuseScale;
float3 unused;
int lightType;
float2 size;
bool twoSided;
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.AreaLightData
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.DirectionalLightData
struct AreaLightData
struct DirectionalLightData
float3 positionWS;
float invSqrAttenuationRadius;
float3 direction;
float diffuseScale;
int shapeType;
float3 forward;
float diffuseScale;
float3 up;
float3 right;
float shadowDimmer;
float2 size;
bool twoSided;
float cosAngle;
float sinAngle;
int shadowIndex;
float unsued;
};
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.PunctualShadowData
// PackingRules = Exact
struct PunctualShadowData
{
float4x4 worldToShadow;
int lightType;
float bias;
float quality;
float unused;
};

};
//
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.PunctualLightData
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.LightData
float3 GetPositionWS(PunctualLightData value)
float3 GetPositionWS(LightData value)
float GetInvSqrAttenuationRadius(PunctualLightData value)
float GetInvSqrAttenuationRadius(LightData value)
float3 GetColor(PunctualLightData value)
float3 GetColor(LightData value)
float GetUseDistanceAttenuation(PunctualLightData value)
float GetAngleScale(LightData value)
return value.useDistanceAttenuation;
return value.angleScale;
float3 GetForward(PunctualLightData value)
float3 GetForward(LightData value)
float GetAngleScale(PunctualLightData value)
float GetAngleOffset(LightData value)
return value.angleScale;
return value.angleOffset;
float3 GetUp(PunctualLightData value)
float3 GetUp(LightData value)
float GetAngleOffset(PunctualLightData value)
float GetDiffuseScale(LightData value)
return value.angleOffset;
return value.diffuseScale;
float3 GetRight(PunctualLightData value)
float3 GetRight(LightData value)
float GetDiffuseScale(PunctualLightData value)
{
return value.diffuseScale;
}
float GetSpecularScale(PunctualLightData value)
float GetSpecularScale(LightData value)
float GetShadowDimmer(PunctualLightData value)
float GetShadowDimmer(LightData value)
int GetShadowIndex(PunctualLightData value)
int GetShadowIndex(LightData value)
int GetIESIndex(PunctualLightData value)
int GetIESIndex(LightData value)
int GetCookieIndex(PunctualLightData value)
int GetCookieIndex(LightData value)
float3 GetUnused(PunctualLightData value)
int GetLightType(LightData value)
{
return value.lightType;
}
float2 GetSize(LightData value)
{
return value.size;
}
bool GetTwoSided(LightData value)
return value.unused;
return value.twoSided;
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.AreaLightData
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.DirectionalLightData
float3 GetPositionWS(AreaLightData value)
float3 GetDirection(DirectionalLightData value)
return value.positionWS;
return value.direction;
float GetInvSqrAttenuationRadius(AreaLightData value)
float GetDiffuseScale(DirectionalLightData value)
return value.invSqrAttenuationRadius;
return value.diffuseScale;
float3 GetColor(AreaLightData value)
float3 GetColor(DirectionalLightData value)
int GetShapeType(AreaLightData value)
float GetSpecularScale(DirectionalLightData value)
return value.shapeType;
return value.specularScale;
float3 GetForward(AreaLightData value)
float GetCosAngle(DirectionalLightData value)
return value.forward;
return value.cosAngle;
float GetDiffuseScale(AreaLightData value)
float GetSinAngle(DirectionalLightData value)
return value.diffuseScale;
return value.sinAngle;
float3 GetUp(AreaLightData value)
int GetShadowIndex(DirectionalLightData value)
return value.up;
return value.shadowIndex;
float GetSpecularScale(AreaLightData value)
float GetUnsued(DirectionalLightData value)
return value.specularScale;
return value.unsued;
float3 GetRight(AreaLightData value)
//
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.PunctualShadowData
//
float4x4 GetWorldToShadow(PunctualShadowData value)
return value.right;
return value.worldToShadow;
float GetShadowDimmer(AreaLightData value)
int GetLightType(PunctualShadowData value)
return value.shadowDimmer;
return value.lightType;
float2 GetSize(AreaLightData value)
float GetBias(PunctualShadowData value)
return value.size;
return value.bias;
bool GetTwoSided(AreaLightData value)
float GetQuality(PunctualShadowData value)
return value.twoSided;
return value.quality;
float GetUnused(AreaLightData value)
float GetUnused(PunctualShadowData value)
{
return value.unused;
}

8
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/Resources/Deferred.shader


Shader "Hidden/HDRenderLoop/Deferred"
{
Properties
{
_SrcBlend("", Float) = 1
_DstBlend("", Float) = 1
}
SubShader
{

Blend[_SrcBlend][_DstBlend]
Blend Off
HLSLPROGRAM
#pragma target 5.0

105
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.cs


namespace UnityEngine.Experimental.ScriptableRenderLoop
{
public class LightLoopSinglePass
namespace SinglePass
string GetKeyword()
//-----------------------------------------------------------------------------
// structure definition
//-----------------------------------------------------------------------------
public class LightLoop
return "LIGHTLOOP_SINGLE_PASS";
}
};
string GetKeyword()
{
return "LIGHTLOOP_SINGLE_PASS";
}
// Static keyword is required here else we get a "DestroyBuffer can only be call in main thread"
static ComputeBuffer s_DirectionalLights;
static ComputeBuffer s_PunctualLightList;
static ComputeBuffer s_EnvLightList;
static ComputeBuffer s_AreaLightList;
static ComputeBuffer s_PunctualShadowList;
void ClearComputeBuffers()
{
if (s_DirectionalLights != null)
s_DirectionalLights.Release();
if (s_PunctualLightList != null)
s_PunctualLightList.Release();
if (s_AreaLightList != null)
s_AreaLightList.Release();
if (s_PunctualShadowList != null)
s_PunctualShadowList.Release();
//-----------------------------------------------------------------------------
// structure definition
//-----------------------------------------------------------------------------
if (s_EnvLightList != null)
s_EnvLightList.Release();
}
public void Rebuild()
{
ClearComputeBuffers();
s_DirectionalLights = new ComputeBuffer(HDRenderLoop.k_MaxDirectionalLightsOnSCreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(DirectionalLightData)));
s_PunctualLightList = new ComputeBuffer(HDRenderLoop.k_MaxPunctualLightsOnSCreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(LightData)));
s_AreaLightList = new ComputeBuffer(HDRenderLoop.k_MaxAreaLightsOnSCreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(LightData)));
s_EnvLightList = new ComputeBuffer(HDRenderLoop.k_MaxEnvLightsOnSCreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(EnvLightData)));
s_PunctualShadowList = new ComputeBuffer(HDRenderLoop.k_MaxShadowOnScreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(PunctualShadowData)));
}
[GenerateHLSL]
public enum ShadowType
{
Spot,
Directional,
Point
};
public void OnDisable()
{
s_DirectionalLights.Release();
s_DirectionalLights = null;
s_PunctualLightList.Release();
s_PunctualLightList = null;
s_AreaLightList.Release();
s_AreaLightList = null;
s_EnvLightList.Release();
s_EnvLightList = null;
s_PunctualShadowList.Release();
s_PunctualShadowList = null;
}
// TODO: we may have to add various parameters here for shadow
// A point light is 6x PunctualShadowData
[GenerateHLSL]
public struct PunctualShadowData
{
// World to ShadowMap matrix
// Include scale and bias for shadow atlas if any
public Matrix4x4 worldToShadow;
public void PushGlobalParams(Camera camera, RenderLoop loop, HDRenderLoop.LightList lightList)
{
s_DirectionalLights.SetData(lightList.directionalLights.ToArray());
s_PunctualLightList.SetData(lightList.punctualLights.ToArray());
s_AreaLightList.SetData(lightList.areaLights.ToArray());
s_EnvLightList.SetData(lightList.envLights.ToArray());
s_PunctualShadowList.SetData(lightList.punctualShadows.ToArray());
public ShadowType shadowType;
public float bias;
public float quality;
public Vector2 unused;
};
Shader.SetGlobalBuffer("_DirectionalLightList", s_DirectionalLights);
Shader.SetGlobalInt("_DirectionalLightCount", lightList.directionalLights.Count);
Shader.SetGlobalBuffer("_PunctualLightList", s_PunctualLightList);
Shader.SetGlobalInt("_PunctualLightCount", lightList.punctualLights.Count);
Shader.SetGlobalBuffer("_AreaLightList", s_AreaLightList);
Shader.SetGlobalInt("_AreaLightCount", lightList.areaLights.Count);
Shader.SetGlobalBuffer("_PunctualShadowList", s_PunctualShadowList);
Shader.SetGlobalBuffer("_EnvLightList", s_EnvLightList);
Shader.SetGlobalInt("_EnvLightCount", lightList.envLights.Count);
}
}
}
}

15
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.hlsl


// It use maxed list of lights of the scene - use just as proof of concept - do not used in regular game
//-----------------------------------------------------------------------------
#include "SinglePass.cs.hlsl"
StructuredBuffer<PunctualLightData> _PunctualLightList;
StructuredBuffer<AreaLightData> _AreaLightList;
StructuredBuffer<EnvLightData> _EnvLightList;
StructuredBuffer<PunctualShadowData> _PunctualShadowList;
StructuredBuffer<DirectionalLightData> _DirectionalLightList;
StructuredBuffer<LightData> _PunctualLightList;
StructuredBuffer<LightData> _AreaLightList;
StructuredBuffer<EnvLightData> _EnvLightList;
StructuredBuffer<PunctualShadowData> _PunctualShadowList;
//TEXTURE2D_ARRAY(_ShadowArray);
//SAMPLER2D_SHADOW(sampler_ShadowArray);

SAMPLERCUBE(sampler_SkyTexture); // NOTE: Sampler could be share here with _EnvTextures. Don't know if the shader compiler will complain...
CBUFFER_START(UnityPerLightLoop)
int _DirectionalLightCount;
int _PunctualLightCount;
int _AreaLightCount;
int _EnvLightCount;

float GetPunctualShadowAttenuation(LightLoopContext lightLoopContext, float3 positionWS, int index, float3 L, float2 unPositionSS)
{
int faceIndex = 0;
if (_PunctualShadowList[index].shadowType == SHADOWTYPE_POINT)
if (_PunctualShadowList[index].lightType == GPULIGHTTYPE_POINT)
{
GetCubeFaceID(L, faceIndex);
}

11
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePassLoop.hlsl


int i = 0; // Declare once to avoid the D3D11 compiler warning.
for (i = 0; i < _DirectionalLightCount; ++i)
{
float3 localDiffuseLighting, localSpecularLighting;
EvaluateBSDF_Directional( context, V, positionWS, prelightData, _DirectionalLightList[i], bsdfData,
localDiffuseLighting, localSpecularLighting);
diffuseLighting += localDiffuseLighting;
specularLighting += localSpecularLighting;
}
for (i = 0; i < _PunctualLightCount; ++i)
{
float3 localDiffuseLighting, localSpecularLighting;

59
Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/Lit.hlsl


#endif
// Reference Lambert diffuse / GGX Specular for IBL and area lights
// #define LIT_DISPLAY_REFERENCE
// #define LIT_DISPLAY_REFERENCE_AREA
// #define LIT_DISPLAY_REFERENCE_IBL
// Use Lambert diffuse instead of Disney diffuse
// #define LIT_DIFFUSE_LAMBERT_BRDF
// Use optimization of Precomputing LambdaV

#ifdef HAS_LIGHTLOOP
//-----------------------------------------------------------------------------
// BSDF share between area light (reference) and punctual light
// BSDF share between directional light, punctual light and area light (reference)
//-----------------------------------------------------------------------------
void BSDF( float3 V, float3 L, float3 positionWS, PreLightData preLightData, BSDFData bsdfData,

}
//-----------------------------------------------------------------------------
// EvaluateBSDF_Directional
//-----------------------------------------------------------------------------
void EvaluateBSDF_Directional( LightLoopContext lightLoopContext,
float3 V, float3 positionWS, PreLightData preLightData, DirectionalLightData lightData, BSDFData bsdfData,
out float3 diffuseLighting,
out float3 specularLighting)
{
float3 L = lightData.direction;
float illuminance = saturate(dot(bsdfData.normalWS, L));
diffuseLighting = float3(0.0, 0.0, 0.0);
specularLighting = float3(0.0, 0.0, 0.0);
[branch] if (lightData.shadowIndex >= 0 && illuminance > 0.0f)
{
float shadowAttenuation = GetPunctualShadowAttenuation(lightLoopContext, positionWS, lightData.shadowIndex, L, preLightData.unPositionSS);
illuminance *= shadowAttenuation;
}
[branch] if (illuminance > 0.0f)
{
BSDF(V, L, positionWS, preLightData, bsdfData, diffuseLighting, specularLighting);
diffuseLighting *= lightData.color * illuminance * lightData.diffuseScale;
specularLighting *= lightData.color * illuminance * lightData.specularScale;
}
}
//-----------------------------------------------------------------------------
float3 V, float3 positionWS, PreLightData preLightData, PunctualLightData lightData, BSDFData bsdfData,
float3 V, float3 positionWS, PreLightData preLightData, LightData lightData, BSDFData bsdfData,
out float3 diffuseLighting,
out float3 specularLighting)
{

// For point light and directional GetAngleAttenuation() return 1
float3 unL = lightData.positionWS - positionWS * lightData.useDistanceAttenuation;
float3 unL = lightData.positionWS - positionWS;
float3 L = normalize(unL);
float attenuation = GetDistanceAttenuation(unL, lightData.invSqrAttenuationRadius);

// EvaluateBSDF_Area - Reference
//-----------------------------------------------------------------------------
void IntegrateGGXAreaRef( float3 V, float3 positionWS, PreLightData preLightData, AreaLightData lightData, BSDFData bsdfData,
void IntegrateGGXAreaRef( float3 V, float3 positionWS, PreLightData preLightData, LightData lightData, BSDFData bsdfData,
out float3 diffuseLighting,
out float3 specularLighting,
uint sampleCount = 512)

float4x4 localToWorld = float4x4(float4(lightData.right, 0.0), float4(lightData.up, 0.0), float4(lightData.forward, 0.0), float4(lightData.positionWS, 1.0));
if (lightData.shapeType == AREASHAPETYPE_SPHERE)
if (lightData.lightType == GPULIGHTTYPE_SPHERE)
else if (lightData.shapeType == AREASHAPETYPE_HEMISPHERE)
else if (lightData.lightType == GPULIGHTTYPE_HEMISPHERE)
else if (lightData.shapeType == AREASHAPETYPE_CYLINDER)
else if (lightData.lightType == GPULIGHTTYPE_CYLINDER)
else if (lightData.shapeType == AREASHAPETYPE_RECTANGLE)
else if (lightData.lightType == GPULIGHTTYPE_RECTANGLE)
else if (lightData.shapeType == AREASHAPETYPE_DISK)
else if (lightData.lightType == GPULIGHTTYPE_DISK)
else if (lightData.shapeType == AREASHAPETYPE_LINE)
else if (lightData.lightType == GPULIGHTTYPE_LINE)
// SampleLine(u, localToWorld, areaLight.lightRadius0, lightPdf, P, Ns);
; // TODO

//-----------------------------------------------------------------------------
void EvaluateBSDF_Area( LightLoopContext lightLoopContext,
float3 V, float3 positionWS, PreLightData preLightData, AreaLightData lightData, BSDFData bsdfData,
float3 V, float3 positionWS, PreLightData preLightData, LightData lightData, BSDFData bsdfData,
#ifdef LIT_DISPLAY_REFERENCE
#ifdef LIT_DISPLAY_REFERENCE_AREA
IntegrateGGXAreaRef(V, positionWS, preLightData, lightData, bsdfData, diffuseLighting, specularLighting);
#else
// TODO: This could be precomputed

float3 V, float3 positionWS, PreLightData preLightData, EnvLightData lightData, BSDFData bsdfData,
out float3 diffuseLighting, out float3 specularLighting, out float2 weight)
{
#ifdef LIT_DISPLAY_REFERENCE
#ifdef LIT_DISPLAY_REFERENCE_IBL
specularLighting = IntegrateSpecularGGXIBLRef(lightLoopContext, V, lightData, bsdfData);

1
Assets/ScriptableRenderLoop/HDRenderLoop/Material/Material.hlsl


#include "Debug.hlsl"
#include "GeometricTools.hlsl"
#include "CommonMaterial.hlsl"
#include "CommonLighting.hlsl"
#include "EntityLighting.hlsl"
//-----------------------------------------------------------------------------

25
Assets/ScriptableRenderLoop/ShaderLibrary/Common.hlsl


return 1.0 / (zBufferParam.z * depth + zBufferParam.w);
}
//-----------------------------------------------------------------------------
// various helper
//-----------------------------------------------------------------------------
// NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping + decal
// In this case this may cause weird artifact.
// GetNdotV return a 'valid' data
float GetNdotV(float3 N, float3 V)
{
return abs(dot(N, V)); // This abs allow to limit artifact
}
// NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping + decal
// In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts.
// but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too).
// Note: This code is not compatible with two sided lighting used in SpeedTree (TODO: investigate).
float GetShiftedNdotV(float3 N, float3 V)
{
// The amount we shift the normal toward the view vector is defined by the dot product.
float shiftAmount = dot(N, V);
N = shiftAmount < 0.0 ? N + V * (-shiftAmount + 1e-5f) : N;
N = normalize(N);
return saturate(dot(N, V)); // TODO: this saturate should not be necessary here
}
#endif // UNITY_COMMON_INCLUDED

22
Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl


// various helper
//-----------------------------------------------------------------------------
// NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping + decal
// In this case this may cause weird artifact.
// GetNdotV return a 'valid' data
float GetNdotV(float3 N, float3 V)
{
return abs(dot(N, V)); // This abs allow to limit artifact
}
// NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping + decal
// In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts.
// but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too).
// Note: This code is not compatible with two sided lighting used in SpeedTree (TODO: investigate).
float GetShiftedNdotV(float3 N, float3 V)
{
// The amount we shift the normal toward the view vector is defined by the dot product.
float shiftAmount = dot(N, V);
N = shiftAmount < 0.0 ? N + V * (-shiftAmount + 1e-5f) : N;
N = normalize(N);
return saturate(dot(N, V)); // TODO: this saturate should not be necessary here
}
// Performs the mapping of the vector 'v' located within the cube of dimensions [-r, r]^3
// to a vector within the sphere of radius 'r', where r = sqrt(r2).
// Modified version of http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass.meta


fileFormatVersion: 2
guid: ef8815a81d7dd9147b37093f06d9977c
folderAsset: yes
timeCreated: 1479218330
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

12
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs.meta


fileFormatVersion: 2
guid: 292dbec5e9c0baa44b67d675e23f4f71
timeCreated: 1479218330
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources.meta


fileFormatVersion: 2
guid: 10637537837597a41861afbe118b246a
folderAsset: yes
timeCreated: 1479306736
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/lightlistbuild.compute.meta


fileFormatVersion: 2
guid: 65af3444cbf4b3747a4dead7ee00cfee
timeCreated: 1479306737
licenseType: Pro
ComputeShaderImporter:
currentAPIMask: 4
userData:
assetBundleName:
assetBundleVariant:

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/scrbound.compute.meta


fileFormatVersion: 2
guid: 728dce960f8a9c44bbc3abb3b851d8f6
timeCreated: 1479306737
licenseType: Pro
ComputeShaderImporter:
currentAPIMask: 4
userData:
assetBundleName:
assetBundleVariant:

442
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/lightlistbuild.compute


// The implementation is based on the demo on "fine pruned tiled lighting" published in GPU Pro 7.
// https://github.com/wolfgangfengel/GPU-Pro-7
#pragma kernel TileLightListGen LIGHTLISTGEN=TileLightListGen
#pragma kernel TileLightListGen_SrcBigTile LIGHTLISTGEN=TileLightListGen_SrcBigTile USE_TWO_PASS_TILED_LIGHTING
float FetchDepth(Texture2D depthTexture, uint2 pixCoord)
{
return 1 - depthTexture.Load(uint3(pixCoord.xy, 0)).x;
}
#include "../TilePass.cs.hlsl"
#include "../LightingConvexHullUtils.hlsl"
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
#include "../SortingComputeUtils.hlsl"
#endif
#define FINE_PRUNING_ENABLED
#define PERFORM_SPHERICAL_INTERSECTION_TESTS
uniform int g_iNrVisibLights;
uniform uint2 g_viDimensions;
uniform float4x4 g_mInvScrProjection;
uniform float4x4 g_mScrProjection;
Texture2D g_depth_tex : register( t0 );
StructuredBuffer<float3> g_vBoundsBuffer : register( t1 );
StructuredBuffer<SFiniteLightData> g_vLightData : register( t2 );
StructuredBuffer<SFiniteLightBound> g_data : register( t3 );
#ifdef USE_TWO_PASS_TILED_LIGHTING
Buffer<uint> g_vBigTileLightList : register( t4 );
#endif
#define NR_THREADS 64
// output buffer
RWBuffer<uint> g_vLightList : register( u0 );
#define MAX_NR_COARSE_ENTRIES 64
#define MAX_NR_PRUNED_ENTRIES 24
groupshared unsigned int coarseList[MAX_NR_COARSE_ENTRIES];
groupshared unsigned int prunedList[MAX_NR_COARSE_ENTRIES]; // temporarily support room for all 64 while in LDS
groupshared uint ldsZMin;
groupshared uint ldsZMax;
groupshared uint lightOffs;
#ifdef FINE_PRUNING_ENABLED
groupshared uint ldsDoesLightIntersect[2];
#endif
groupshared int ldsNrLightsFinal;
groupshared int ldsModelListCount[NR_LIGHT_MODELS]; // since NR_LIGHT_MODELS is 2
#ifdef PERFORM_SPHERICAL_INTERSECTION_TESTS
groupshared uint lightOffsSph;
#endif
//float GetLinearDepth(float3 vP)
//{
// float4 v4Pres = mul(g_mInvScrProjection, float4(vP,1.0));
// return v4Pres.z / v4Pres.w;
//}
float GetLinearDepth(float zDptBufSpace) // 0 is near 1 is far
{
float3 vP = float3(0.0f,0.0f,zDptBufSpace);
float4 v4Pres = mul(g_mInvScrProjection, float4(vP,1.0));
return v4Pres.z / v4Pres.w;
}
float3 GetViewPosFromLinDepth(float2 v2ScrPos, float fLinDepth)
{
float fSx = g_mScrProjection[0].x;
float fCx = g_mScrProjection[0].z;
float fSy = g_mScrProjection[1].y;
float fCy = g_mScrProjection[1].z;
#if USE_LEFTHAND_CAMERASPACE
return fLinDepth*float3( ((v2ScrPos.x-fCx)/fSx), ((v2ScrPos.y-fCy)/fSy), 1.0 );
#else
return fLinDepth*float3( -((v2ScrPos.x+fCx)/fSx), -((v2ScrPos.y+fCy)/fSy), 1.0 );
#endif
}
float GetOnePixDiagWorldDistAtDepthOne()
{
float fSx = g_mScrProjection[0].x;
float fSy = g_mScrProjection[1].y;
return length( float2(1.0/fSx,1.0/fSy) );
}
#ifdef PERFORM_SPHERICAL_INTERSECTION_TESTS
int SphericalIntersectionTests(uint threadID, int iNrCoarseLights, float2 screenCoordinate);
#endif
#ifdef FINE_PRUNING_ENABLED
void FinePruneLights(uint threadID, int iNrCoarseLights, uint2 viTilLL, float4 vLinDepths);
#endif
[numthreads(NR_THREADS, 1, 1)]
void LIGHTLISTGEN(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID)
{
uint2 tileIDX = u3GroupID.xy;
uint t=threadID;
if(t<MAX_NR_COARSE_ENTRIES)
prunedList[t]=0;
uint iWidth = g_viDimensions.x;
uint iHeight = g_viDimensions.y;
uint nrTilesX = (iWidth+15)/16;
uint nrTilesY = (iHeight+15)/16;
// build tile scr boundary
const uint uFltMax = 0x7f7fffff; // FLT_MAX as a uint
if(t==0)
{
ldsZMin = uFltMax;
ldsZMax = 0;
lightOffs = 0;
}
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
GroupMemoryBarrierWithGroupSync();
#endif
uint2 viTilLL = 16*tileIDX;
// establish min and max depth first
float dpt_mi=asfloat(uFltMax), dpt_ma=0.0;
float4 vLinDepths;
{
// Fetch depths and calculate min/max
[unroll]
for(int i = 0; i < 4; i++)
{
int idx = i * NR_THREADS + t;
uint2 uCrd = min( uint2(viTilLL.x+(idx&0xf), viTilLL.y+(idx>>4)), uint2(iWidth-1, iHeight-1) );
const float fDepth = FetchDepth(g_depth_tex, uCrd);
vLinDepths[i] = GetLinearDepth(fDepth);
if(fDepth<VIEWPORT_SCALE_Z) // if not skydome
{
dpt_mi = min(fDepth, dpt_mi);
dpt_ma = max(fDepth, dpt_ma);
}
}
InterlockedMax(ldsZMax, asuint(dpt_ma));
InterlockedMin(ldsZMin, asuint(dpt_mi));
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
GroupMemoryBarrierWithGroupSync();
#endif
}
float3 vTileLL = float3(viTilLL.x/(float) iWidth, viTilLL.y/(float) iHeight, asfloat(ldsZMin));
float3 vTileUR = float3((viTilLL.x+16)/(float) iWidth, (viTilLL.y+16)/(float) iHeight, asfloat(ldsZMax));
vTileUR.xy = min(vTileUR.xy,float2(1.0,1.0)).xy;
// build coarse list using AABB
#ifdef USE_TWO_PASS_TILED_LIGHTING
int NrBigTilesX = (nrTilesX+3)>>2;
const int bigTileIdx = (tileIDX.y>>2)*NrBigTilesX + (tileIDX.x>>2); // map the idx to 64x64 tiles
int nrBigTileLights = g_vBigTileLightList[MAX_NR_BIGTILE_LIGHTS_PLUSONE*bigTileIdx+0];
for(int l0=(int) t; l0<(int) nrBigTileLights; l0 += NR_THREADS)
{
int l = g_vBigTileLightList[MAX_NR_BIGTILE_LIGHTS_PLUSONE*bigTileIdx+l0+1];
#else
for(int l=(int) t; l<(int) g_iNrVisibLights; l += NR_THREADS)
{
#endif
const float3 vMi = g_vBoundsBuffer[l];
const float3 vMa = g_vBoundsBuffer[l+g_iNrVisibLights];
if( all(vMa>vTileLL) && all(vMi<vTileUR))
{
unsigned int uInc = 1;
unsigned int uIndex;
InterlockedAdd(lightOffs, uInc, uIndex);
if(uIndex<MAX_NR_COARSE_ENTRIES) coarseList[uIndex] = l; // add to light list
}
}
#ifdef FINE_PRUNING_ENABLED
if(t<2) ldsDoesLightIntersect[t] = 0;
#endif
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
GroupMemoryBarrierWithGroupSync();
#endif
int iNrCoarseLights = min(lightOffs,MAX_NR_COARSE_ENTRIES);
#ifdef PERFORM_SPHERICAL_INTERSECTION_TESTS
iNrCoarseLights = SphericalIntersectionTests( t, iNrCoarseLights, float2(min(viTilLL.xy+uint2(16/2,16/2), uint2(iWidth-1, iHeight-1))) );
#endif
#ifndef FINE_PRUNING_ENABLED
{
if((int)t<iNrCoarseLights) prunedList[t] = coarseList[t];
if(t==0) ldsNrLightsFinal=iNrCoarseLights;
}
#else
{
// initializes ldsNrLightsFinal with the number of accepted lights.
// all accepted entries delivered in prunedList[].
FinePruneLights(t, iNrCoarseLights, viTilLL, vLinDepths);
}
#endif
//
if(t<NR_LIGHT_MODELS) ldsModelListCount[t]=0;
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
GroupMemoryBarrierWithGroupSync();
#endif
int nrLightsCombinedList = min(ldsNrLightsFinal,MAX_NR_COARSE_ENTRIES);
for(int i=t; i<nrLightsCombinedList; i+=NR_THREADS)
{
InterlockedAdd(ldsModelListCount[ g_vLightData[ prunedList[i] ].lightModel ], 1);
}
// sort lights (gives a more efficient execution in both deferred and tiled forward lighting).
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
SORTLIST(prunedList, nrLightsCombinedList, MAX_NR_COARSE_ENTRIES, t, NR_THREADS);
//MERGESORTLIST(prunedList, coarseList, nrLightsCombinedList, t, NR_THREADS);
#endif
// write lights to global buffers
int localOffs=0;
int offs = tileIDX.y*nrTilesX + tileIDX.x;
for(int m=0; m<NR_LIGHT_MODELS; m++)
{
int nrLightsFinal = ldsModelListCount[ m ];
int nrLightsFinalClamped = nrLightsFinal<MAX_NR_PRUNED_ENTRIES ? nrLightsFinal : MAX_NR_PRUNED_ENTRIES;
const int nrDWords = ((nrLightsFinalClamped+1)+1)>>1;
for(int l=(int) t; l<(int) nrDWords; l += NR_THREADS)
{
uint uLow = l==0 ? nrLightsFinalClamped : prunedList[2*l-1+localOffs];
uint uHigh = prunedList[2*l+0+localOffs];
g_vLightList[16*offs + l] = (uLow&0xffff) | (uHigh<<16);
}
localOffs += nrLightsFinal;
offs += (nrTilesX*nrTilesY);
}
}
#ifdef PERFORM_SPHERICAL_INTERSECTION_TESTS
int SphericalIntersectionTests(uint threadID, int iNrCoarseLights, float2 screenCoordinate)
{
lightOffsSph = 0;
// make a copy of coarseList in prunedList.
for(int l=threadID; l<iNrCoarseLights; l+=NR_THREADS)
prunedList[l]=coarseList[l];
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
GroupMemoryBarrierWithGroupSync();
#endif
#if USE_LEFTHAND_CAMERASPACE
float3 V = GetViewPosFromLinDepth( screenCoordinate, 1.0);
#else
float3 V = GetViewPosFromLinDepth( screenCoordinate, -1.0);
#endif
float onePixDiagDist = GetOnePixDiagWorldDistAtDepthOne();
float halfTileSizeAtZDistOne = 8*onePixDiagDist; // scale by half a tile
for(int l=threadID; l<iNrCoarseLights; l+=NR_THREADS)
{
SFiniteLightBound lightData = g_data[coarseList[l]];
if( DoesSphereOverlapTile(V, halfTileSizeAtZDistOne, lightData.center.xyz, lightData.radius) )
{
unsigned int uIndex;
InterlockedAdd(lightOffsSph, 1, uIndex);
coarseList[uIndex]=prunedList[l]; // read from the original copy of coarseList which is backed up in prunedList
}
}
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
GroupMemoryBarrierWithGroupSync();
#endif
return lightOffsSph;
}
#endif
#ifdef FINE_PRUNING_ENABLED
// initializes ldsNrLightsFinal with the number of accepted lights.
// all accepted entries delivered in prunedList[].
void FinePruneLights(uint threadID, int iNrCoarseLights, uint2 viTilLL, float4 vLinDepths)
{
uint t = threadID;
uint iWidth = g_viDimensions.x;
uint iHeight = g_viDimensions.y;
uint uLightsFlags[2] = {0,0};
int l=0;
// need this outer loop even on xb1 and ps4 since direct lights and
// reflection lights are kept in separate regions.
while(l<iNrCoarseLights)
{
// fetch light
int idxCoarse = l<iNrCoarseLights ? coarseList[l] : 0;
uint uLgtType = l<iNrCoarseLights ? g_vLightData[idxCoarse].lightType : 0;
// spot
while(l<iNrCoarseLights && uLgtType==SPOT_LIGHT)
{
SFiniteLightData lightData = g_vLightData[idxCoarse];
const bool bIsSpotDisc = (lightData.flags&IS_CIRCULAR_SPOT_SHAPE)!=0;
// serially check 4 pixels
uint uVal = 0;
for(int i=0; i<4; i++)
{
int idx = t + i*NR_THREADS;
uint2 uPixLoc = min(uint2(viTilLL.x+(idx&0xf), viTilLL.y+(idx>>4)), uint2(iWidth-1, iHeight-1));
float3 vVPos = GetViewPosFromLinDepth(uPixLoc + float2(0.5,0.5), vLinDepths[i]);
// check pixel
float3 fromLight = vVPos-lightData.lightPos.xyz;
float distSq = dot(fromLight,fromLight);
const float fSclProj = dot(fromLight, lightData.lightAxisZ.xyz); // spotDir = lightData.lightAxisZ.xyz
float2 V = abs( float2( dot(fromLight, lightData.lightAxisX.xyz), dot(fromLight, lightData.lightAxisY.xyz) ) );
float fDist2D = bIsSpotDisc ? length(V) : max(V.x,V.y);
if( all( float2(lightData.radiusSq, fSclProj) > float2(distSq, fDist2D*lightData.cotan) ) ) uVal = 1;
}
uLightsFlags[l<32 ? 0 : 1] |= (uVal<<(l&31));
++l; idxCoarse = l<iNrCoarseLights ? coarseList[l] : 0;
uLgtType = l<iNrCoarseLights ? g_vLightData[idxCoarse].lightType : 0;
}
// sphere
while(l<iNrCoarseLights && uLgtType==SPHERE_LIGHT)
{
SFiniteLightData lightData = g_vLightData[idxCoarse];
// serially check 4 pixels
uint uVal = 0;
for(int i=0; i<4; i++)
{
int idx = t + i*NR_THREADS;
uint2 uPixLoc = min(uint2(viTilLL.x+(idx&0xf), viTilLL.y+(idx>>4)), uint2(iWidth-1, iHeight-1));
float3 vVPos = GetViewPosFromLinDepth(uPixLoc + float2(0.5,0.5), vLinDepths[i]);
// check pixel
float3 vLp = lightData.lightPos.xyz;
float3 toLight = vLp - vVPos;
float distSq = dot(toLight,toLight);
if(lightData.radiusSq>distSq) uVal = 1;
}
uLightsFlags[l<32 ? 0 : 1] |= (uVal<<(l&31));
++l; idxCoarse = l<iNrCoarseLights ? coarseList[l] : 0;
uLgtType = l<iNrCoarseLights ? g_vLightData[idxCoarse].lightType : 0;
}
// Box
while(l<iNrCoarseLights && uLgtType==BOX_LIGHT)
{
SFiniteLightData lightData = g_vLightData[idxCoarse];
// serially check 4 pixels
uint uVal = 0;
for(int i=0; i<4; i++)
{
int idx = t + i*NR_THREADS;
uint2 uPixLoc = min(uint2(viTilLL.x+(idx&0xf), viTilLL.y+(idx>>4)), uint2(iWidth-1, iHeight-1));
float3 vVPos = GetViewPosFromLinDepth(uPixLoc + float2(0.5,0.5), vLinDepths[i]);
// check pixel
float3 toLight = lightData.lightPos.xyz - vVPos;
float3 dist = float3( dot(toLight, lightData.lightAxisX), dot(toLight, lightData.lightAxisY), dot(toLight, lightData.lightAxisZ) );
dist = (abs(dist) - lightData.boxInnerDist) * lightData.boxInvRange; // not as efficient as it could be
if( max(max(dist.x, dist.y), dist.z)<1 ) uVal = 1; // but allows us to not write out OuterDists
}
uLightsFlags[l<32 ? 0 : 1] |= (uVal<<(l&31));
++l; idxCoarse = l<iNrCoarseLights ? coarseList[l] : 0;
uLgtType = l<iNrCoarseLights ? g_vLightData[idxCoarse].lightType : 0;
}
// in case we have some corrupt data make sure we terminate
if(uLgtType>=MAX_TYPES) ++l;
}
InterlockedOr(ldsDoesLightIntersect[0], uLightsFlags[0]);
InterlockedOr(ldsDoesLightIntersect[1], uLightsFlags[1]);
if(t==0) ldsNrLightsFinal = 0;
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)
GroupMemoryBarrierWithGroupSync();
#endif
if(t<(uint) iNrCoarseLights && (ldsDoesLightIntersect[t<32 ? 0 : 1]&(1<<(t&31)))!=0 )
{
unsigned int uInc = 1;
unsigned int uIndex;
InterlockedAdd(ldsNrLightsFinal, uInc, uIndex);
if(uIndex<MAX_NR_COARSE_ENTRIES) prunedList[uIndex] = coarseList[t]; // we allow up to 64 pruned lights while stored in LDS.
}
}
#endif

465
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/Resources/scrbound.compute


// The implementation is based on the demo on "fine pruned tiled lighting" published in GPU Pro 7.
// https://github.com/wolfgangfengel/GPU-Pro-7
#pragma kernel ScreenBoundsAABB
#include "../TilePass.cs.hlsl"
uniform int g_iNrVisibLights;
uniform float4x4 g_mInvProjection;
uniform float4x4 g_mProjection;
StructuredBuffer<SFiniteLightBound> g_data : register( t0 );
#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0
#define NR_THREADS 64
// output buffer
RWStructuredBuffer<float3> g_vBoundsBuffer : register( u0 );
#define MAX_PNTS 9 // strictly this should be 10=6+4 but we get more wavefronts and 10 seems to never hit (fingers crossed)
// However, worst case the plane that would be skipped if such an extreme case ever happened would be backplane
// clipping gets skipped which doesn't cause any errors.
// LDS (2496 bytes)
groupshared float posX[MAX_PNTS*8*2];
groupshared float posY[MAX_PNTS*8*2];
groupshared float posZ[MAX_PNTS*8*2];
groupshared float posW[MAX_PNTS*8*2];
groupshared unsigned int clipFlags[48];
unsigned int GetClip(const float4 P);
int ClipAgainstPlane(const int iSrcIndex, const int iNrSrcVerts, const int subLigt, const int p);
void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, out float2 vMax, float4x4 InvProjection, float3 pos_view_space, float r);
#include "../LightingConvexHullUtils.hlsl"
[numthreads(NR_THREADS, 1, 1)]
void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID)
{
uint groupID = u3GroupID.x;
//uint vindex = groupID * NR_THREADS + threadID;
unsigned int g = groupID;
unsigned int t = threadID;
const int subLigt = (int) (t/8);
const int lgtIndex = subLigt+(int) g*8;
const int sideIndex = (int) (t%8);
SFiniteLightBound lgtDat = g_data[lgtIndex];
const float3 boxX = lgtDat.boxAxisX.xyz;
const float3 boxY = lgtDat.boxAxisY.xyz;
const float3 boxZ = -lgtDat.boxAxisZ.xyz; // flip axis (so it points away from the light direction for a spot-light)
const float3 center = lgtDat.center.xyz;
const float radius = lgtDat.radius;
const float2 scaleXY = lgtDat.scaleXY;
{
if(sideIndex<6 && lgtIndex<(int) g_iNrVisibLights) // mask 2 out of 8 threads
{
float3 q0, q1, q2, q3;
GetQuad(q0, q1, q2, q3, boxX, boxY, boxZ, center, scaleXY, sideIndex);
const float4 vP0 = mul(g_mProjection, float4(q0, 1));
const float4 vP1 = mul(g_mProjection, float4(q1, 1));
const float4 vP2 = mul(g_mProjection, float4(q2, 1));
const float4 vP3 = mul(g_mProjection, float4(q3, 1));
// test vertices of one quad (of the convex hull) for intersection
const unsigned int uFlag0 = GetClip(vP0);
const unsigned int uFlag1 = GetClip(vP1);
const unsigned int uFlag2 = GetClip(vP2);
const unsigned int uFlag3 = GetClip(vP3);
const float4 vPnts[] = {vP0, vP1, vP2, vP3};
// screen-space AABB of one quad (assuming no intersection)
float3 vMin, vMax;
for(int k=0; k<4; k++)
{
float fW = vPnts[k].w;
float fS = fW<0 ? -1 : 1;
float fWabs = fW<0 ? (-fW) : fW;
fW = fS * (fWabs<FLT_EPSILON ? FLT_EPSILON : fWabs);
float3 vP = float3(vPnts[k].x/fW, vPnts[k].y/fW, vPnts[k].z/fW);
if(k==0) { vMin=vP; vMax=vP; }
vMax = max(vMax, vP); vMin = min(vMin, vP);
}
clipFlags[subLigt*6+sideIndex] = (uFlag0<<0) | (uFlag1<<6) | (uFlag2<<12) | (uFlag3<<18);
// store in clip buffer (only use these vMin and vMax if light is 100% visible in which case clipping isn't needed)
posX[subLigt*MAX_PNTS*2 + sideIndex] = vMin.x;
posY[subLigt*MAX_PNTS*2 + sideIndex] = vMin.y;
posZ[subLigt*MAX_PNTS*2 + sideIndex] = vMin.z;
posX[subLigt*MAX_PNTS*2 + sideIndex + 6] = vMax.x;
posY[subLigt*MAX_PNTS*2 + sideIndex + 6] = vMax.y;
posZ[subLigt*MAX_PNTS*2 + sideIndex + 6] = vMax.z;
}
}
// if not XBONE and not PLAYSTATION4 we need a memorybarrier here
// since we can't rely on the gpu cores being 64 wide.
// We need a pound define around this.
GroupMemoryBarrierWithGroupSync();
{
int f=0;
if(sideIndex==0 && lgtIndex<(int) g_iNrVisibLights)
{
// quick acceptance or rejection
unsigned int uCollectiveAnd = (unsigned int) -1;
unsigned int uCollectiveOr = 0;
for(f=0; f<6; f++)
{
unsigned int uFlagAnd = clipFlags[subLigt*6+f]&0x3f;
unsigned int uFlagOr = uFlagAnd;
for(int i=1; i<4; i++)
{
unsigned int uClipBits = (clipFlags[subLigt*6+f]>>(i*6))&0x3f;
uFlagAnd &= uClipBits;
uFlagOr |= uClipBits;
}
uCollectiveAnd &= uFlagAnd;
uCollectiveOr |= uFlagOr;
}
bool bSetBoundYet = false;
float3 vMin=0.0, vMax=0.0;
if(uCollectiveAnd!=0 || uCollectiveOr==0) // all invisible or all visible (early out)
{
if(uCollectiveOr==0) // all visible
{
for(f=0; f<6; f++)
{
const int sideIndex = f;
float3 vFaceMi = float3(posX[subLigt*MAX_PNTS*2 + sideIndex + 0], posY[subLigt*MAX_PNTS*2 + sideIndex + 0], posZ[subLigt*MAX_PNTS*2 + sideIndex + 0]);
float3 vFaceMa = float3(posX[subLigt*MAX_PNTS*2 + sideIndex + 6], posY[subLigt*MAX_PNTS*2 + sideIndex + 6], posZ[subLigt*MAX_PNTS*2 + sideIndex + 6]);
for(int k=0; k<2; k++)
{
float3 vP = k==0 ? vFaceMi : vFaceMa;
if(f==0 && k==0) { vMin=vP; vMax=vP; }
vMax = max(vMax, vP); vMin = min(vMin, vP);
}
}
bSetBoundYet=true;
}
}
else // :( need true clipping
{
for(f=0; f<6; f++)
{
float3 q0, q1, q2, q3;
GetQuad(q0, q1, q2, q3, boxX, boxY, boxZ, center, scaleXY, f);
// 4 vertices to a quad of the convex hull in post projection space
const float4 vP0 = mul(g_mProjection, float4(q0, 1));
const float4 vP1 = mul(g_mProjection, float4(q1, 1));
const float4 vP2 = mul(g_mProjection, float4(q2, 1));
const float4 vP3 = mul(g_mProjection, float4(q3, 1));
int iSrcIndex = 0;
int offs = iSrcIndex*MAX_PNTS+subLigt*MAX_PNTS*2;
// fill up source clip buffer with the quad
posX[offs+0]=vP0.x; posX[offs+1]=vP1.x; posX[offs+2]=vP2.x; posX[offs+3]=vP3.x;
posY[offs+0]=vP0.y; posY[offs+1]=vP1.y; posY[offs+2]=vP2.y; posY[offs+3]=vP3.y;
posZ[offs+0]=vP0.z; posZ[offs+1]=vP1.z; posZ[offs+2]=vP2.z; posZ[offs+3]=vP3.z;
posW[offs+0]=vP0.w; posW[offs+1]=vP1.w; posW[offs+2]=vP2.w; posW[offs+3]=vP3.w;
int iNrSrcVerts = 4;
// do true clipping
for(int p=0; p<6; p++)
{
const int nrVertsDst = ClipAgainstPlane(iSrcIndex, iNrSrcVerts, subLigt, p);
iSrcIndex = 1-iSrcIndex;
iNrSrcVerts = nrVertsDst;
if(iNrSrcVerts<3 || iNrSrcVerts>=MAX_PNTS) break;
}
// final clipped convex primitive is in src buffer
if(iNrSrcVerts>2)
{
int offs_src = iSrcIndex*MAX_PNTS+subLigt*MAX_PNTS*2;
for(int k=0; k<iNrSrcVerts; k++)
{
float4 vCur = float4(posX[offs_src+k], posY[offs_src+k], posZ[offs_src+k], posW[offs_src+k]);
// project and apply toward AABB
float3 vP = float3(vCur.x/vCur.w, vCur.y/vCur.w, vCur.z/vCur.w);
if(!bSetBoundYet) { vMin=vP; vMax=vP; bSetBoundYet=true; }
vMax = max(vMax, vP); vMin = min(vMin, vP);
}
}
}
////////////////////// look for camera frustum verts that need to be included. That is frustum vertices inside the convex hull for the light
int i=0;
for(i=0; i<8; i++) // establish 8 camera frustum vertices
{
float3 vVertPSpace = float3((i&1)!=0 ? 1 : (-1), (i&2)!=0 ? 1 : (-1), (i&4)!=0 ? 1 : 0);
float4 v4ViewSpace = mul(g_mInvProjection, float4(vVertPSpace,1));
float3 vViewSpace = float3(v4ViewSpace.x/v4ViewSpace.w, v4ViewSpace.y/v4ViewSpace.w, v4ViewSpace.z/v4ViewSpace.w);
posX[subLigt*MAX_PNTS*2 + i] = vViewSpace.x;
posY[subLigt*MAX_PNTS*2 + i] = vViewSpace.y;
posZ[subLigt*MAX_PNTS*2 + i] = vViewSpace.z;
}
// determine which camera frustum vertices are inside the convex hull
uint uVisibFl = 0xff;
for(f=0; f<6; f++)
{
float3 vP0, vN;
GetPlane(vP0, vN, boxX, boxY, boxZ, center, scaleXY, f);
for(i=0; i<8; i++)
{
float3 vViewSpace = float3(posX[subLigt*MAX_PNTS*2 + i], posY[subLigt*MAX_PNTS*2 + i], posZ[subLigt*MAX_PNTS*2 + i]);
uVisibFl &= ( dot(vViewSpace-vP0, vN)<0 ? 0xff : (~(1<<i)) );
}
}
// apply camera frustum vertices inside the convex hull to the AABB
for(i=0; i<8; i++)
{
if((uVisibFl&(1<<i))!=0)
{
float3 vP = float3((i&1)!=0 ? 1 : (-1), (i&2)!=0 ? 1 : (-1), (i&4)!=0 ? 1 : 0);
if(!bSetBoundYet) { vMin=vP; vMax=vP; bSetBoundYet=true; }
vMax = max(vMax, vP); vMin = min(vMin, vP);
}
}
}
// determine AABB bound in [-1;1]x[-1;1] screen space using bounding sphere.
// Use the result to make our already established AABB from the convex hull
// potentially tighter.
if(!bSetBoundYet)
{
// set the AABB off-screen
vMin = float3(-3,-3,-3);
vMax = float3(-2,-2,-2);
}
else
{
//if((center.z+radius)<0.0)
if( length(center)>radius)
{
float2 vMi, vMa;
bool2 bMi, bMa;
CalcBound(bMi, bMa, vMi, vMa, g_mInvProjection, center, radius);
vMin.xy = bMi ? max(vMin.xy, vMi) : vMin.xy;
vMax.xy = bMa ? min(vMax.xy, vMa) : vMax.xy;
}
#if USE_LEFTHAND_CAMERASPACE
if((center.z-radius)>0.0)
{
float4 vPosF = mul(g_mProjection, float4(0,0,center.z-radius,1));
vMin.z = max(vMin.z, vPosF.z/vPosF.w);
}
if((center.z+radius)>0.0)
{
float4 vPosB = mul(g_mProjection, float4(0,0,center.z+radius,1));
vMax.z = min(vMax.z, vPosB.z/vPosB.w);
}
#else
if((center.z+radius)<0.0)
{
float4 vPosF = mul(g_mProjection, float4(0,0,center.z+radius,1));
vMin.z = max(vMin.z, vPosF.z/vPosF.w);
}
if((center.z-radius)<0.0)
{
float4 vPosB = mul(g_mProjection, float4(0,0,center.z-radius,1));
vMax.z = min(vMax.z, vPosB.z/vPosB.w);
}
#endif
else
{
vMin = float3(-3,-3,-3);
vMax = float3(-2,-2,-2);
}
}
// we should consider doing a look-up here into a max depth mip chain
// to see if the light is occluded: vMin.z*VIEWPORT_SCALE_Z > MipTexelMaxDepth
//g_vBoundsBuffer[lgtIndex+0] = float3(0.5*vMin.x+0.5, -0.5*vMax.y+0.5, vMin.z*VIEWPORT_SCALE_Z);
//g_vBoundsBuffer[lgtIndex+g_iNrVisibLights] = float3(0.5*vMax.x+0.5, -0.5*vMin.y+0.5, vMax.z*VIEWPORT_SCALE_Z);
// changed for unity
g_vBoundsBuffer[lgtIndex+0] = float3(0.5*vMin.x+0.5, 0.5*vMin.y+0.5, vMin.z*VIEWPORT_SCALE_Z);
g_vBoundsBuffer[lgtIndex+(int) g_iNrVisibLights] = float3(0.5*vMax.x+0.5, 0.5*vMax.y+0.5, vMax.z*VIEWPORT_SCALE_Z);
}
}
}
float4 GenNewVert(const float4 vVisib, const float4 vInvisib, const int p);
int ClipAgainstPlane(const int iSrcIndex, const int iNrSrcVerts, const int subLigt, const int p)
{
int offs_src = iSrcIndex*MAX_PNTS+subLigt*MAX_PNTS*2;
int offs_dst = (1-iSrcIndex)*MAX_PNTS+subLigt*MAX_PNTS*2;
float4 vPrev = float4(posX[offs_src+(iNrSrcVerts-1)], posY[offs_src+(iNrSrcVerts-1)], posZ[offs_src+(iNrSrcVerts-1)], posW[offs_src+(iNrSrcVerts-1)]);
int nrVertsDst = 0;
unsigned int uMask = (1<<p);
bool bIsPrevVisib = (GetClip(vPrev)&uMask)==0;
for(int i=0; i<iNrSrcVerts; i++)
{
float4 vCur = float4(posX[offs_src+i], posY[offs_src+i], posZ[offs_src+i], posW[offs_src+i]);
bool bIsCurVisib = (GetClip(vCur)&uMask)==0;
if( (bIsCurVisib && !bIsPrevVisib) || (!bIsCurVisib && bIsPrevVisib) )
{
//assert(nrVertsDst<MAX_PNTS);
if(nrVertsDst<MAX_PNTS)
{
// generate new vertex
float4 vNew = GenNewVert(bIsCurVisib ? vCur : vPrev, bIsCurVisib ? vPrev : vCur, p);
posX[offs_dst+nrVertsDst]=vNew.x; posY[offs_dst+nrVertsDst]=vNew.y; posZ[offs_dst+nrVertsDst]=vNew.z; posW[offs_dst+nrVertsDst]=vNew.w;
++nrVertsDst;
}
}
if(bIsCurVisib)
{
//assert(nrVertsDst<MAX_PNTS);
if(nrVertsDst<MAX_PNTS)
{
posX[offs_dst+nrVertsDst]=vCur.x; posY[offs_dst+nrVertsDst]=vCur.y; posZ[offs_dst+nrVertsDst]=vCur.z; posW[offs_dst+nrVertsDst]=vCur.w;
++nrVertsDst;
}
}
vPrev = vCur;
bIsPrevVisib = bIsCurVisib;
}
return nrVertsDst;
}
unsigned int GetClip(const float4 P)
{
//-P.w <= P.x <= P.w
return ((P.x<-P.w)?1:0) | ((P.x>P.w)?2:0) | ((P.y<-P.w)?4:0) | ((P.y>P.w)?8:0) | ((P.z<0)?16:0) | ((P.z>P.w)?32:0);
}
float4 GenNewVert(const float4 vVisib, const float4 vInvisib, const int p)
{
const float fS = p==4 ? 0 : ((p&1)==0 ? -1 : 1);
const int index = ((uint) p)/2;
float x1 = index==0 ? vVisib.x : (index==1 ? vVisib.y : vVisib.z);
float x0 = index==0 ? vInvisib.x : (index==1 ? vInvisib.y : vInvisib.z);
//fS*((vVisib.w-vInvisib.w)*t + vInvisib.w) = (x1-x0)*t + x0;
const float fT = (fS*vInvisib.w-x0)/((x1-x0) - fS*(vVisib.w-vInvisib.w));
float4 vNew = vVisib*fT + vInvisib*(1-fT);
// just to be really anal we make sure the clipped against coordinate is precise
if(index==0) vNew.x = fS*vNew.w;
else if(index==1) vNew.y = fS*vNew.w;
else vNew.z = fS*vNew.w;
return vNew;
}
float4 TransformPlaneToPostSpace(float4x4 InvProjection, float4 plane)
{
return mul(plane, InvProjection);
}
float4 EvalPlanePair(float2 posXY_in, float r)
{
// rotate by 90 degrees to avoid potential division by zero
bool bMustFlip = abs(posXY_in.y)<abs(posXY_in.x);
float2 posXY = bMustFlip ? float2(-posXY_in.y, posXY_in.x) : posXY_in;
float fLenSQ = dot(posXY, posXY);
float D = posXY.y * sqrt(fLenSQ - r*r);
float4 res;
res.x = (-r*posXY.x - D) / fLenSQ;
res.z = (-r*posXY.x + D) / fLenSQ;
res.y = (-r-res.x*posXY.x) / posXY.y;
res.w = (-r-res.z*posXY.x) / posXY.y;
// rotate back by 90 degrees
res = bMustFlip ? float4(res.y, -res.x, res.w, -res.z) : res;
return res;
}
void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, out float2 vMax, float4x4 InvProjection, float3 pos_view_space, float r)
{
float4 planeX = EvalPlanePair(float2(pos_view_space.x, pos_view_space.z), r);
float4 planeY = EvalPlanePair(float2(pos_view_space.y, pos_view_space.z), r);
#if USE_LEFTHAND_CAMERASPACE
planeX = planeX.zwxy; // need to swap left/right and top/bottom planes when using left hand system
planeY = planeY.zwxy;
#endif
bIsMinValid = bool2(planeX.z<0, planeY.z<0);
bIsMaxValid = bool2((-planeX.x)<0, (-planeY.x)<0);
// hopefully the compiler takes zeros into account
// should be the case since the transformation in TransformPlaneToPostSpace()
// is done using multiply-adds and not dot product instructions.
float4 planeX0 = TransformPlaneToPostSpace(InvProjection, float4(planeX.x, 0, planeX.y, 0));
float4 planeX1 = TransformPlaneToPostSpace(InvProjection, float4(planeX.z, 0, planeX.w, 0));
float4 planeY0 = TransformPlaneToPostSpace(InvProjection, float4(0, planeY.x, planeY.y, 0));
float4 planeY1 = TransformPlaneToPostSpace(InvProjection, float4(0, planeY.z, planeY.w, 0));
// convert planes to the forms (1,0,0,D) and (0,1,0,D)
// 2D bound is given by -D components
float2 A = -float2(planeX0.w / planeX0.x, planeY0.w / planeY0.y);
float2 B = -float2(planeX1.w / planeX1.x, planeY1.w / planeY1.y);
// Bound is complete
vMin = B;
vMax = A;
}

128
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/LightingConvexHullUtils.hlsl


#ifndef __LIGHTINGCONVEXHULLUTILS_H__
#define __LIGHTINGCONVEXHULLUTILS_H__
#include "TilePass.cs.hlsl"
float3 GetHullVertex(const float3 boxX, const float3 boxY, const float3 boxZ, const float3 center, const float2 scaleXY, const int p)
{
const bool bIsTopVertex = (p&4)!=0;
float3 vScales = float3( ((p&1)!=0 ? 1.0f : (-1.0f))*(bIsTopVertex ? scaleXY.x : 1.0), ((p&2)!=0 ? 1.0f : (-1.0f))*(bIsTopVertex ? scaleXY.y : 1.0), (p&4)!=0 ? 1.0f : (-1.0f) );
return (vScales.x*boxX + vScales.y*boxY + vScales.z*boxZ) + center;
}
void GetHullEdge(out int idx0, out int idx_twin, out float3 vP0, out float3 vE0, const int e0, const float3 boxX, const float3 boxY, const float3 boxZ, const float3 center, const float2 scaleXY)
{
int iAxis = e0>>2;
int iSwizzle = e0&0x3;
bool bIsSwizzleOneOrTwo = ((iSwizzle-1)&0x2)==0;
const int i0 = iAxis==0 ? (2*iSwizzle+0) : ( iAxis==1 ? (iSwizzle+(iSwizzle&2)) : iSwizzle);
const int i1 = i0 + (1<<iAxis);
const bool bSwap = iAxis==0 ? (!bIsSwizzleOneOrTwo) : (iAxis==1 ? false : bIsSwizzleOneOrTwo);
idx0 = bSwap ? i1 : i0;
idx_twin = bSwap ? i0 : i1;
float3 p0 = GetHullVertex(boxX, boxY, boxZ, center, scaleXY, idx0);
float3 p1 = GetHullVertex(boxX, boxY, boxZ, center, scaleXY, idx_twin);
vP0 = p0;
vE0 = p1-p0;
}
void GetQuad(out float3 p0, out float3 p1, out float3 p2, out float3 p3, const float3 boxX, const float3 boxY, const float3 boxZ, const float3 center, const float2 scaleXY, const int sideIndex)
{
//const int iAbsSide = (sideIndex == 0 || sideIndex == 1) ? 0 : ((sideIndex == 2 || sideIndex == 3) ? 1 : 2);
const int iAbsSide = min(sideIndex>>1, 2);
const float fS = (sideIndex & 1) != 0 ? 1 : (-1);
float3 vA = fS*(iAbsSide == 0 ? boxX : (iAbsSide == 1 ? (-boxY) : boxZ));
float3 vB = fS*(iAbsSide == 0 ? (-boxY) : (iAbsSide == 1 ? (-boxX) : (-boxY)));
float3 vC = iAbsSide == 0 ? boxZ : (iAbsSide == 1 ? boxZ : (-boxX));
bool bIsTopQuad = iAbsSide == 2 && (sideIndex & 1) != 0; // in this case all 4 verts get scaled.
bool bIsSideQuad = (iAbsSide == 0 || iAbsSide == 1); // if side quad only two verts get scaled (impacts q1 and q2)
if (bIsTopQuad) { vB *= scaleXY.y; vC *= scaleXY.x; }
float3 vA2 = vA;
float3 vB2 = vB;
if (bIsSideQuad) { vA2 *= (iAbsSide == 0 ? scaleXY.x : scaleXY.y); vB2 *= (iAbsSide == 0 ? scaleXY.y : scaleXY.x); }
// delivered counterclockwise in right hand space and clockwise in left hand space
p0 = center + (vA + vB - vC); // center + vA is center of face when scaleXY is 1.0
p1 = center + (vA - vB - vC);
p2 = center + (vA2 - vB2 + vC);
p3 = center + (vA2 + vB2 + vC);
}
void GetPlane(out float3 p0, out float3 vN, const float3 boxX, const float3 boxY, const float3 boxZ, const float3 center, const float2 scaleXY, const int sideIndex)
{
//const int iAbsSide = (sideIndex == 0 || sideIndex == 1) ? 0 : ((sideIndex == 2 || sideIndex == 3) ? 1 : 2);
const int iAbsSide = min(sideIndex>>1, 2);
const float fS = (sideIndex & 1) != 0 ? 1 : (-1);
float3 vA = fS*(iAbsSide == 0 ? boxX : (iAbsSide == 1 ? (-boxY) : boxZ));
float3 vB = fS*(iAbsSide == 0 ? (-boxY) : (iAbsSide == 1 ? (-boxX) : (-boxY)));
float3 vC = iAbsSide == 0 ? boxZ : (iAbsSide == 1 ? boxZ : (-boxX));
bool bIsTopQuad = iAbsSide == 2 && (sideIndex & 1) != 0; // in this case all 4 verts get scaled.
bool bIsSideQuad = (iAbsSide == 0 || iAbsSide == 1); // if side quad only two verts get scaled (impacts q1 and q2)
if (bIsTopQuad) { vB *= scaleXY.y; vC *= scaleXY.x; }
float3 vA2 = vA;
float3 vB2 = vB;
if (bIsSideQuad) { vA2 *= (iAbsSide == 0 ? scaleXY.x : scaleXY.y); vB2 *= (iAbsSide == 0 ? scaleXY.y : scaleXY.x); }
p0 = center + (vA + vB - vC); // center + vA is center of face when scaleXY is 1.0
float3 vNout = cross( vB2, 0.5*(vA-vA2) - vC );
#if USE_LEFTHAND_CAMERASPACE
vNout = -vNout;
#endif
vN = vNout;
}
float4 GetPlaneEq(const float3 boxX, const float3 boxY, const float3 boxZ, const float3 center, const float2 scaleXY, const int sideIndex)
{
float3 p0, vN;
GetPlane(p0, vN, boxX, boxY, boxZ, center, scaleXY, sideIndex);
return float4(vN, -dot(vN,p0));
}
bool DoesSphereOverlapTile(float3 dir, float halfTileSizeAtZDistOne, float3 sphCen, float sphRadiusIn)
{
float3 V = dir; // ray direction down center of tile (does not need to be normalized).
#if 1
float3 maxZdir = float3(-sphCen.z*sphCen.x, -sphCen.z*sphCen.y, sphCen.x*sphCen.x + sphCen.y*sphCen.y); // cross(sphCen,cross(Zaxis,sphCen))
float len = length(maxZdir);
float scalarProj = len>0.0001 ? (maxZdir.z/len) : len; // since len>=(maxZdir.z/len) we can use len as an approximate value when len<=epsilon
float offs = scalarProj*sphRadiusIn;
#else
float offs = sphRadiusIn; // more false positives due to larger radius but works too
#endif
// enlarge sphere so it overlaps the center of the tile assuming it overlaps the tile to begin with.
#if USE_LEFTHAND_CAMERASPACE
float sphRadius = sphRadiusIn + (sphCen.z+offs)*halfTileSizeAtZDistOne;
#else
float sphRadius = sphRadiusIn - (sphCen.z-offs)*halfTileSizeAtZDistOne;
#endif
float a = dot(V,V);
float CdotV = dot(sphCen,V);
float c = dot(sphCen,sphCen) - sphRadius*sphRadius;
float fDescDivFour = CdotV*CdotV - a*c;
return c<0 || (fDescDivFour>0 && CdotV>0); // if ray hits bounding sphere
}
#endif

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/LightingConvexHullUtils.hlsl.meta


fileFormatVersion: 2
guid: 7a025524906ace949b463f8f79a03b5c
timeCreated: 1479344028
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

117
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/SortingComputeUtils.hlsl


#ifndef __SORTINGCOMPUTEUTILS_H__
#define __SORTINGCOMPUTEUTILS_H__
unsigned int LimitPow2AndClamp(unsigned int value_in, unsigned int maxValue)
{
#if 0
unsigned int value = 1;
while(value<value_in && (value<<1)<=maxValue)
value<<=1;
return value_in==0 ? 0 : value;
#else
uint valpw2 = value_in==0 ? 0 : (1<<firstbithigh(value_in)); // firstbithigh(0) returns -1
valpw2 = max(valpw2, valpw2<<(valpw2!=value_in ? 1 : 0)); // max() just in case of overflow
return min(valpw2, maxValue);
#endif
}
// have to make this sort routine a macro unfortunately because hlsl doesn't take
// groupshared memory of unspecified length as an input parameter to a function.
// maxcapacity_in must be a power of two.
// all data from length_in and up to closest power of two will be filled with 0xffffffff
#define SORTLIST(data, length_in, maxcapacity_in, localThreadID_in, nrthreads_in) \
{ \
int length=(int) length_in, maxcapacity=(int) maxcapacity_in, localThreadID=(int) localThreadID_in, nrthreads=(int) nrthreads_in; \
\
const int N = (const int) LimitPow2AndClamp((unsigned int) length, (uint) maxcapacity); \
for(int t=length+localThreadID; t<N; t+=nrthreads) { data[t]=0xffffffff; } \
GroupMemoryBarrierWithGroupSync(); \
\
for(int k=2; k<=N; k=2*k) \
{ \
for(int j=k>>1; j>0; j=j>>1) \
{ \
for(int i=localThreadID; i<N; i+=nrthreads) \
{ \
int ixj=i^j; \
if((ixj)>i) \
{ \
const unsigned int Avalue = data[i]; \
const unsigned int Bvalue = data[ixj]; \
\
const bool mustSwap = ((i&k)!=0^(Avalue>Bvalue)) && Avalue!=Bvalue; \
if(mustSwap) \
{ \
data[i]=Bvalue; \
data[ixj]=Avalue; \
} \
} \
} \
\
GroupMemoryBarrierWithGroupSync(); \
} \
} \
}
// have to make this sort routine a macro unfortunately because hlsl doesn't take
// groupshared memory of unspecified length as an input parameter to a function.
// merge-sort is not in-place so two buffers are required: data and tmpdata.
// These must both have a capacity of at least length_in entries and initial
// input is assumed to be in data and results will be delivered in data.
#define MERGESORTLIST(data, tmpdata, length_in, localThreadID_in, nrthreads_in) \
{ \
int length=(int) length_in, localThreadID=(int) localThreadID_in, nrthreads=(int) nrthreads_in; \
\
for(int curr_size=1; curr_size<=length-1; curr_size = 2*curr_size) \
{ \
for(int left_start=localThreadID*(2*curr_size); left_start<(length-1); left_start+=nrthreads*(2*curr_size)) \
{ \
int mid = left_start + curr_size - 1; \
int right_end = min(left_start + 2*curr_size - 1, length-1); \
{ \
int l=left_start, m=mid, r=right_end; \
\
int i, j, k; \
\
int ol = l; \
int or = m+1; \
int sl = m - l + 1; \
int sr = r - m; \
\
for(int i=l; i<=r; i++) tmpdata[i] = data[i]; \
\
i = 0; j = 0; k = l; \
while (i < sl && j < sr) \
{ \
const uint lVal = tmpdata[ol+i]; \
const uint rVal = tmpdata[or+j]; \
bool pickLeft = lVal <= rVal; \
i = pickLeft ? (i+1) : i; \
j = pickLeft ? j : (j+1); \
data[k] = pickLeft ? lVal : rVal; \
k++; \
} \
\
while (i < sl) \
{ \
data[k] = tmpdata[ol+i]; \
i++; k++; \
} \
\
while (j < sr) \
{ \
data[k] = tmpdata[or+j]; \
j++; k++; \
} \
} \
} \
\
GroupMemoryBarrierWithGroupSync(); \
} \
}
#endif

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/SortingComputeUtils.hlsl.meta


fileFormatVersion: 2
guid: 66b6d3029d5cdb2498af99c4cc141e85
timeCreated: 1479344254
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

148
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs.hlsl


//
// This file was automatically generated from Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs. Please don't edit by hand.
//
#ifndef TILEPASS_CS_HLSL
#define TILEPASS_CS_HLSL
//
// UnityEngine.Experimental.ScriptableRenderLoop.TilePass.LightDefinitions: static fields
//
#define MAX_NR_LIGHTS_PER_CAMERA (1024)
#define MAX_NR_BIGTILE_LIGHTS_PLUSONE (512)
#define VIEWPORT_SCALE_Z (1)
#define USE_LEFTHAND_CAMERASPACE (0)
#define IS_CIRCULAR_SPOT_SHAPE (1)
#define HAS_COOKIE_TEXTURE (2)
#define IS_BOX_PROJECTED (4)
#define HAS_SHADOW (8)
#define MAX_TYPES (3)
#define SPOT_LIGHT (0)
#define SPHERE_LIGHT (1)
#define BOX_LIGHT (2)
#define DIRECTIONAL_LIGHT (3)
#define NR_LIGHT_MODELS (2)
#define DIRECT_LIGHT (0)
#define REFLECTION_LIGHT (1)
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.TilePass.SFiniteLightBound
// PackingRules = Exact
struct SFiniteLightBound
{
float3 boxAxisX;
float3 boxAxisY;
float3 boxAxisZ;
float3 center;
float2 scaleXY;
float radius;
};
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.TilePass.SFiniteLightData
// PackingRules = Exact
struct SFiniteLightData
{
float3 lightPos;
int flags;
float3 lightAxisX;
uint lightType;
float3 lightAxisY;
float radiusSq;
float3 lightAxisZ;
float cotan;
float3 color;
uint lightModel;
float3 boxInnerDist;
float unusued;
float3 boxInvRange;
float unused2;
};
//
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.TilePass.SFiniteLightBound
//
float3 GetBoxAxisX(SFiniteLightBound value)
{
return value.boxAxisX;
}
float3 GetBoxAxisY(SFiniteLightBound value)
{
return value.boxAxisY;
}
float3 GetBoxAxisZ(SFiniteLightBound value)
{
return value.boxAxisZ;
}
float3 GetCenter(SFiniteLightBound value)
{
return value.center;
}
float2 GetScaleXY(SFiniteLightBound value)
{
return value.scaleXY;
}
float GetRadius(SFiniteLightBound value)
{
return value.radius;
}
//
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.TilePass.SFiniteLightData
//
float3 GetLightPos(SFiniteLightData value)
{
return value.lightPos;
}
int GetFlags(SFiniteLightData value)
{
return value.flags;
}
float3 GetLightAxisX(SFiniteLightData value)
{
return value.lightAxisX;
}
uint GetLightType(SFiniteLightData value)
{
return value.lightType;
}
float3 GetLightAxisY(SFiniteLightData value)
{
return value.lightAxisY;
}
float GetRadiusSq(SFiniteLightData value)
{
return value.radiusSq;
}
float3 GetLightAxisZ(SFiniteLightData value)
{
return value.lightAxisZ;
}
float GetCotan(SFiniteLightData value)
{
return value.cotan;
}
float3 GetColor(SFiniteLightData value)
{
return value.color;
}
uint GetLightModel(SFiniteLightData value)
{
return value.lightModel;
}
float3 GetBoxInnerDist(SFiniteLightData value)
{
return value.boxInnerDist;
}
float GetUnusued(SFiniteLightData value)
{
return value.unusued;
}
float3 GetBoxInvRange(SFiniteLightData value)
{
return value.boxInvRange;
}
float GetUnused2(SFiniteLightData value)
{
return value.unused2;
}
#endif

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs.hlsl.meta


fileFormatVersion: 2
guid: a9b85578530535241bc9f921bee3ad98
timeCreated: 1479344047
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

674
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/TilePass/TilePass.cs


using UnityEngine;
using UnityEngine.Experimental.Rendering;
using System;
namespace UnityEngine.Experimental.ScriptableRenderLoop
{
namespace TilePass
{
//-----------------------------------------------------------------------------
// structure definition
//-----------------------------------------------------------------------------
[GenerateHLSL]
public class LightDefinitions
{
public static int MAX_NR_LIGHTS_PER_CAMERA = 1024;
public static int MAX_NR_BIGTILE_LIGHTS_PLUSONE = 512; // may be overkill but the footprint is 2 bits per pixel using uint16.
public static float VIEWPORT_SCALE_Z = 1.0f;
// enable unity's original left-hand shader camera space (right-hand internally in unity).
public static int USE_LEFTHAND_CAMERASPACE = 0;
// flags
public static int IS_CIRCULAR_SPOT_SHAPE = 1;
public static int HAS_COOKIE_TEXTURE = 2;
public static int IS_BOX_PROJECTED = 4;
public static int HAS_SHADOW = 8;
// types
public static int MAX_TYPES = 3;
public static int SPOT_LIGHT = 0;
public static int SPHERE_LIGHT = 1;
public static int BOX_LIGHT = 2;
public static int DIRECTIONAL_LIGHT = 3;
// direct lights and reflection probes for now
public static int NR_LIGHT_MODELS = 2;
public static int DIRECT_LIGHT = 0;
public static int REFLECTION_LIGHT = 1;
}
[GenerateHLSL]
public struct SFiniteLightBound
{
public Vector3 boxAxisX;
public Vector3 boxAxisY;
public Vector3 boxAxisZ;
public Vector3 center; // a center in camera space inside the bounding volume of the light source.
public Vector2 scaleXY;
public float radius;
};
[GenerateHLSL]
public struct SFiniteLightData
{
public Vector3 lightPos;
public int flags;
public Vector3 lightAxisX;
public uint lightType;
public Vector3 lightAxisY;
public float radiusSq;
public Vector3 lightAxisZ; // spot +Z axis
public float cotan;
public Vector3 color;
public uint lightModel; // DIRECT_LIGHT=0, REFLECTION_LIGHT=1
public Vector3 boxInnerDist;
public float unusued;
public Vector3 boxInvRange;
public float unused2;
};
public class LightLoop
{
string GetKeyword()
{
return "LIGHTLOOP_SINGLE_PASS";
}
/*
public const int MaxNumLights = HDRenderLoop.k_MaxPunctualLightsOnSCreen + HDRenderLoop.k_MaxAreaLightsOnSCreen + HDRenderLoop.k_MaxEnvLightsOnSCreen;
public const int MaxNumDirLights = 2;
public const float FltMax = 3.402823466e+38F;
ComputeShader buildScreenAABBShader;
ComputeShader buildPerTileLightListShader; // FPTL
ComputeShader buildPerBigTileLightListShader;
ComputeShader buildPerVoxelLightListShader; // clustered
private static int s_GenAABBKernel;
private static int s_GenListPerTileKernel;
private static int s_GenListPerVoxelKernel;
private static int s_ClearVoxelAtomicKernel;
private static ComputeBuffer s_LightDataBuffer;
private static ComputeBuffer s_ConvexBoundsBuffer;
private static ComputeBuffer s_AABBBoundsBuffer;
private static ComputeBuffer s_LightList;
private static ComputeBuffer s_DirLightList;
private static ComputeBuffer s_BigTileLightList; // used for pre-pass coarse culling on 64x64 tiles
private static int s_GenListPerBigTileKernel;
// clustered light list specific buffers and data begin
public bool enableClustered = false;
public bool disableFptlWhenClustered = false; // still useful on opaques
public bool enableBigTilePrepass = true;
public bool enableDrawLightBoundsDebug = false;
public bool enableDrawTileDebug = false;
public bool enableComputeLightEvaluation = false;
const bool k_UseDepthBuffer = true; // only has an impact when EnableClustered is true (requires a depth-prepass)
const bool k_UseAsyncCompute = true; // should not use on mobile
const int k_Log2NumClusters = 6; // accepted range is from 0 to 6. NumClusters is 1<<g_iLog2NumClusters
const float k_ClustLogBase = 1.02f; // each slice 2% bigger than the previous
float m_ClustScale;
private static ComputeBuffer s_PerVoxelLightLists;
private static ComputeBuffer s_PerVoxelOffset;
private static ComputeBuffer s_PerTileLogBaseTweak;
private static ComputeBuffer s_GlobalLightListAtomic;
// clustered light list specific buffers and data end
const int k_TileSize = 16;
*/
bool usingFptl
{
get
{
/*
bool isEnabledMSAA = false;
Debug.Assert(!isEnabledMSAA || enableClustered);
bool disableFptl = (disableFptlWhenClustered && enableClustered) || isEnabledMSAA;
return !disableFptl;
*/
return true;
}
}
// Local function
void ClearComputeBuffers()
{
/*
ReleaseResolutionDependentBuffers();
if (s_AABBBoundsBuffer != null)
s_AABBBoundsBuffer.Release();
if (s_ConvexBoundsBuffer != null)
s_ConvexBoundsBuffer.Release();
if (s_LightDataBuffer != null)
s_LightDataBuffer.Release();
if (s_DirLightList != null)
s_DirLightList.Release();
if (enableClustered)
{
if (s_GlobalLightListAtomic != null)
s_GlobalLightListAtomic.Release();
}
*/
}
public void Rebuild()
{
/*
ClearComputeBuffers();
buildScreenAABBShader = Resources.Load<ComputeShader>("scrbound");
buildPerTileLightListShader = Resources.Load<ComputeShader>("lightlistbuild");
buildPerBigTileLightListShader = Resources.Load<ComputeShader>("lightlistbuild-bigtile");
buildPerVoxelLightListShader = Resources.Load<ComputeShader>("lightlistbuild-clustered");
s_GenAABBKernel = buildScreenAABBShader.FindKernel("ScreenBoundsAABB");
s_GenListPerTileKernel = buildPerTileLightListShader.FindKernel(enableBigTilePrepass ? "TileLightListGen_SrcBigTile" : "TileLightListGen");
s_AABBBoundsBuffer = new ComputeBuffer(2 * MaxNumLights, 3 * sizeof(float));
s_ConvexBoundsBuffer = new ComputeBuffer(MaxNumLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(SFiniteLightBound)));
s_LightDataBuffer = new ComputeBuffer(MaxNumLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(SFiniteLightData)));
s_DirLightList = new ComputeBuffer(MaxNumDirLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(DirectionalLight)));
buildScreenAABBShader.SetBuffer(s_GenAABBKernel, "g_data", s_ConvexBoundsBuffer);
buildPerTileLightListShader.SetBuffer(s_GenListPerTileKernel, "g_vBoundsBuffer", s_AABBBoundsBuffer);
buildPerTileLightListShader.SetBuffer(s_GenListPerTileKernel, "g_vLightData", s_LightDataBuffer);
buildPerTileLightListShader.SetBuffer(s_GenListPerTileKernel, "g_data", s_ConvexBoundsBuffer);
if (enableClustered)
{
var kernelName = enableBigTilePrepass ? (k_UseDepthBuffer ? "TileLightListGen_DepthRT_SrcBigTile" : "TileLightListGen_NoDepthRT_SrcBigTile") : (k_UseDepthBuffer ? "TileLightListGen_DepthRT" : "TileLightListGen_NoDepthRT");
s_GenListPerVoxelKernel = buildPerVoxelLightListShader.FindKernel(kernelName);
s_ClearVoxelAtomicKernel = buildPerVoxelLightListShader.FindKernel("ClearAtomic");
buildPerVoxelLightListShader.SetBuffer(s_GenListPerVoxelKernel, "g_vBoundsBuffer", s_AABBBoundsBuffer);
buildPerVoxelLightListShader.SetBuffer(s_GenListPerVoxelKernel, "g_vLightData", s_LightDataBuffer);
buildPerVoxelLightListShader.SetBuffer(s_GenListPerVoxelKernel, "g_data", s_ConvexBoundsBuffer);
s_GlobalLightListAtomic = new ComputeBuffer(1, sizeof(uint));
}
if (enableBigTilePrepass)
{
s_GenListPerBigTileKernel = buildPerBigTileLightListShader.FindKernel("BigTileLightListGen");
buildPerBigTileLightListShader.SetBuffer(s_GenListPerBigTileKernel, "g_vBoundsBuffer", s_AABBBoundsBuffer);
buildPerBigTileLightListShader.SetBuffer(s_GenListPerBigTileKernel, "g_vLightData", s_LightDataBuffer);
buildPerBigTileLightListShader.SetBuffer(s_GenListPerBigTileKernel, "g_data", s_ConvexBoundsBuffer);
}
*/
}
public void OnDisable()
{/*
// TODO: do something for Resources.Load<ComputeShader> ?
s_AABBBoundsBuffer.Release();
s_ConvexBoundsBuffer.Release();
s_LightDataBuffer.Release();
ReleaseResolutionDependentBuffers();
s_DirLightList.Release();
if (enableClustered)
{
s_GlobalLightListAtomic.Release();
}
*/
}
/*
public bool NeedResize()
{
return s_LightList == null || (s_BigTileLightList == null && enableBigTilePrepass) || (s_PerVoxelLightLists == null && enableClustered);
}
public void ReleaseResolutionDependentBuffers()
{
if (s_LightList != null)
s_LightList.Release();
if (enableClustered)
{
if (s_PerVoxelLightLists != null)
s_PerVoxelLightLists.Release();
if (s_PerVoxelOffset != null)
s_PerVoxelOffset.Release();
if (k_UseDepthBuffer && s_PerTileLogBaseTweak != null)
s_PerTileLogBaseTweak.Release();
}
if (enableBigTilePrepass)
{
if (s_BigTileLightList != null)
s_BigTileLightList.Release();
}
}
int NumLightIndicesPerClusteredTile()
{
return 8 * (1 << k_Log2NumClusters); // total footprint for all layers of the tile (measured in light index entries)
}
public void AllocResolutionDependentBuffers(int width, int height)
{
var nrTilesX = (width + k_TileSize - 1) / k_TileSize;
var nrTilesY = (height + k_TileSize - 1) / k_TileSize;
var nrTiles = nrTilesX * nrTilesY;
const int capacityUShortsPerTile = 32;
const int dwordsPerTile = (capacityUShortsPerTile + 1) >> 1; // room for 31 lights and a nrLights value.
s_LightList = new ComputeBuffer(LightDefinitions.NR_LIGHT_MODELS * dwordsPerTile * nrTiles, sizeof(uint)); // enough list memory for a 4k x 4k display
if (enableClustered)
{
s_PerVoxelOffset = new ComputeBuffer(LightDefinitions.NR_LIGHT_MODELS * (1 << k_Log2NumClusters) * nrTiles, sizeof(uint));
s_PerVoxelLightLists = new ComputeBuffer(NumLightIndicesPerClusteredTile() * nrTiles, sizeof(uint));
if (k_UseDepthBuffer)
{
s_PerTileLogBaseTweak = new ComputeBuffer(nrTiles, sizeof(float));
}
}
if (enableBigTilePrepass)
{
var nrBigTilesX = (width + 63) / 64;
var nrBigTilesY = (height + 63) / 64;
var nrBigTiles = nrBigTilesX * nrBigTilesY;
s_BigTileLightList = new ComputeBuffer(LightDefinitions.MAX_NR_BIGTILE_LIGHTS_PLUSONE * nrBigTiles, sizeof(uint));
}
}
*/
/*
int GenerateSourceLightBuffers(Camera camera, CullResults inputs)
{
var probes = inputs.visibleReflectionProbes;
//ReflectionProbe[] probes = Object.FindObjectsOfType<ReflectionProbe>();
var numModels = (int)LightDefinitions.NR_LIGHT_MODELS;
var numVolTypes = (int)LightDefinitions.MAX_TYPES;
var numEntries = new int[numModels, numVolTypes];
var offsets = new int[numModels, numVolTypes];
var numEntries2nd = new int[numModels, numVolTypes];
// first pass. Figure out how much we have of each and establish offsets
foreach (var cl in inputs.visibleLights)
{
var volType = cl.lightType == LightType.Spot ? LightDefinitions.SPOT_LIGHT : (cl.lightType == LightType.Point ? LightDefinitions.SPHERE_LIGHT : -1);
if (volType >= 0) ++numEntries[LightDefinitions.DIRECT_LIGHT, volType];
}
foreach (var rl in probes)
{
var volType = LightDefinitions.BOX_LIGHT; // always a box for now
if (rl.texture != null) ++numEntries[LightDefinitions.REFLECTION_LIGHT, volType];
}
// add decals here too similar to the above
// establish offsets
for (var m = 0; m < numModels; m++)
{
offsets[m, 0] = m == 0 ? 0 : (numEntries[m - 1, numVolTypes - 1] + offsets[m - 1, numVolTypes - 1]);
for (var v = 1; v < numVolTypes; v++) offsets[m, v] = numEntries[m, v - 1] + offsets[m, v - 1];
}
var numLights = inputs.visibleLights.Length;
var numProbes = probes.Length;
var numVolumes = numLights + numProbes;
var lightData = new SFiniteLightData[numVolumes];
var boundData = new SFiniteLightBound[numVolumes];
var worldToView = WorldToCamera(camera);
bool isNegDeterminant = Vector3.Dot(worldToView.GetColumn(0), Vector3.Cross(worldToView.GetColumn(1), worldToView.GetColumn(2))) < 0.0f; // 3x3 Determinant.
uint shadowLightIndex = 0;
foreach (var cl in inputs.visibleLights)
{
var range = cl.range;
var lightToWorld = cl.localToWorld;
Vector3 lightPos = lightToWorld.GetColumn(3);
var bound = new SFiniteLightBound();
var light = new SFiniteLightData();
bound.boxAxisX.Set(1, 0, 0);
bound.boxAxisY.Set(0, 1, 0);
bound.boxAxisZ.Set(0, 0, 1);
bound.scaleXY.Set(1.0f, 1.0f);
bound.radius = range;
light.flags = 0;
light.recipRange = 1.0f / range;
light.color.Set(cl.finalColor.r, cl.finalColor.g, cl.finalColor.b);
light.sliceIndex = 0;
light.lightModel = (uint)LightDefinitions.DIRECT_LIGHT;
light.shadowLightIndex = shadowLightIndex;
shadowLightIndex++;
var bHasCookie = cl.light.cookie != null;
var bHasShadow = cl.light.shadows != LightShadows.None;
var idxOut = 0;
if (cl.lightType == LightType.Spot)
{
var isCircularSpot = !bHasCookie;
if (!isCircularSpot) // square spots always have cookie
{
light.sliceIndex = m_CookieTexArray.FetchSlice(cl.light.cookie);
}
Vector3 lightDir = lightToWorld.GetColumn(2); // Z axis in world space
// represents a left hand coordinate system in world space
Vector3 vx = lightToWorld.GetColumn(0); // X axis in world space
Vector3 vy = lightToWorld.GetColumn(1); // Y axis in world space
var vz = lightDir; // Z axis in world space
// transform to camera space (becomes a left hand coordinate frame in Unity since Determinant(worldToView)<0)
vx = worldToView.MultiplyVector(vx);
vy = worldToView.MultiplyVector(vy);
vz = worldToView.MultiplyVector(vz);
const float pi = 3.1415926535897932384626433832795f;
const float degToRad = (float)(pi / 180.0);
var sa = cl.light.spotAngle;
var cs = Mathf.Cos(0.5f * sa * degToRad);
var si = Mathf.Sin(0.5f * sa * degToRad);
var ta = cs > 0.0f ? (si / cs) : FltMax;
var cota = si > 0.0f ? (cs / si) : FltMax;
//const float cotasa = l.GetCotanHalfSpotAngle();
// apply nonuniform scale to OBB of spot light
var squeeze = true;//sa < 0.7f * 90.0f; // arb heuristic
var fS = squeeze ? ta : si;
bound.center = worldToView.MultiplyPoint(lightPos + ((0.5f * range) * lightDir)); // use mid point of the spot as the center of the bounding volume for building screen-space AABB for tiled lighting.
light.lightAxisX = vx;
light.lightAxisY = vy;
light.lightAxisZ = vz;
// scale axis to match box or base of pyramid
bound.boxAxisX = (fS * range) * vx;
bound.boxAxisY = (fS * range) * vy;
bound.boxAxisZ = (0.5f * range) * vz;
// generate bounding sphere radius
var fAltDx = si;
var fAltDy = cs;
fAltDy = fAltDy - 0.5f;
//if(fAltDy<0) fAltDy=-fAltDy;
fAltDx *= range; fAltDy *= range;
var altDist = Mathf.Sqrt(fAltDy * fAltDy + (isCircularSpot ? 1.0f : 2.0f) * fAltDx * fAltDx);
bound.radius = altDist > (0.5f * range) ? altDist : (0.5f * range); // will always pick fAltDist
bound.scaleXY = squeeze ? new Vector2(0.01f, 0.01f) : new Vector2(1.0f, 1.0f);
// fill up ldata
light.lightType = (uint)LightDefinitions.SPOT_LIGHT;
light.lightPos = worldToView.MultiplyPoint(lightPos);
light.radiusSq = range * range;
light.penumbra = cs;
light.cotan = cota;
light.flags |= (isCircularSpot ? LightDefinitions.IS_CIRCULAR_SPOT_SHAPE : 0);
light.flags |= (bHasCookie ? LightDefinitions.HAS_COOKIE_TEXTURE : 0);
light.flags |= (bHasShadow ? LightDefinitions.HAS_SHADOW : 0);
int i = LightDefinitions.DIRECT_LIGHT, j = LightDefinitions.SPOT_LIGHT;
idxOut = numEntries2nd[i, j] + offsets[i, j]; ++numEntries2nd[i, j];
}
else if (cl.lightType == LightType.Point)
{
if (bHasCookie)
{
light.sliceIndex = m_CubeCookieTexArray.FetchSlice(cl.light.cookie);
}
bound.center = worldToView.MultiplyPoint(lightPos);
bound.boxAxisX.Set(range, 0, 0);
bound.boxAxisY.Set(0, range, 0);
bound.boxAxisZ.Set(0, 0, isNegDeterminant ? (-range) : range); // transform to camera space (becomes a left hand coordinate frame in Unity since Determinant(worldToView)<0)
bound.scaleXY.Set(1.0f, 1.0f);
bound.radius = range;
// represents a left hand coordinate system in world space since det(worldToView)<0
var lightToView = worldToView * lightToWorld;
Vector3 vx = lightToView.GetColumn(0);
Vector3 vy = lightToView.GetColumn(1);
Vector3 vz = lightToView.GetColumn(2);
// fill up ldata
light.lightType = (uint)LightDefinitions.SPHERE_LIGHT;
light.lightPos = bound.center;
light.radiusSq = range * range;
light.lightAxisX = vx;
light.lightAxisY = vy;
light.lightAxisZ = vz;
light.flags |= (bHasCookie ? LightDefinitions.HAS_COOKIE_TEXTURE : 0);
light.flags |= (bHasShadow ? LightDefinitions.HAS_SHADOW : 0);
int i = LightDefinitions.DIRECT_LIGHT, j = LightDefinitions.SPHERE_LIGHT;
idxOut = numEntries2nd[i, j] + offsets[i, j]; ++numEntries2nd[i, j];
}
else
{
//Assert(false);
}
// next light
if (cl.lightType == LightType.Spot || cl.lightType == LightType.Point)
{
boundData[idxOut] = bound;
lightData[idxOut] = light;
}
}
var numLightsOut = offsets[LightDefinitions.DIRECT_LIGHT, numVolTypes - 1] + numEntries[LightDefinitions.DIRECT_LIGHT, numVolTypes - 1];
// probe.m_BlendDistance
// Vector3f extents = 0.5*Abs(probe.m_BoxSize);
// C center of rendered refl box <-- GetComponent (Transform).GetPosition() + m_BoxOffset;
// cube map capture point: GetComponent (Transform).GetPosition()
// shader parameter min and max are C+/-(extents+blendDistance)
foreach (var rl in probes)
{
var cubemap = rl.texture;
// always a box for now
if (cubemap == null)
continue;
var bndData = new SFiniteLightBound();
var lgtData = new SFiniteLightData();
var idxOut = 0;
lgtData.flags = 0;
var bnds = rl.bounds;
var boxOffset = rl.center; // reflection volume offset relative to cube map capture point
var blendDistance = rl.blendDistance;
float imp = rl.importance;
var mat = rl.localToWorld;
//Matrix4x4 mat = rl.transform.localToWorldMatrix;
Vector3 cubeCapturePos = mat.GetColumn(3); // cube map capture position in world space
// implicit in CalculateHDRDecodeValues() --> float ints = rl.intensity;
var boxProj = (rl.boxProjection != 0);
var decodeVals = rl.hdr;
//Vector4 decodeVals = rl.CalculateHDRDecodeValues();
// C is reflection volume center in world space (NOT same as cube map capture point)
var e = bnds.extents; // 0.5f * Vector3.Max(-boxSizes[p], boxSizes[p]);
//Vector3 C = bnds.center; // P + boxOffset;
var C = mat.MultiplyPoint(boxOffset); // same as commented out line above when rot is identity
//Vector3 posForShaderParam = bnds.center - boxOffset; // gives same as rl.GetComponent<Transform>().position;
var posForShaderParam = cubeCapturePos; // same as commented out line above when rot is identity
var combinedExtent = e + new Vector3(blendDistance, blendDistance, blendDistance);
Vector3 vx = mat.GetColumn(0);
Vector3 vy = mat.GetColumn(1);
Vector3 vz = mat.GetColumn(2);
// transform to camera space (becomes a left hand coordinate frame in Unity since Determinant(worldToView)<0)
vx = worldToView.MultiplyVector(vx);
vy = worldToView.MultiplyVector(vy);
vz = worldToView.MultiplyVector(vz);
var Cw = worldToView.MultiplyPoint(C);
if (boxProj) lgtData.flags |= LightDefinitions.IS_BOX_PROJECTED;
lgtData.lightPos = Cw;
lgtData.lightAxisX = vx;
lgtData.lightAxisY = vy;
lgtData.lightAxisZ = vz;
lgtData.localCubeCapturePoint = -boxOffset;
lgtData.probeBlendDistance = blendDistance;
lgtData.lightIntensity = decodeVals.x;
lgtData.decodeExp = decodeVals.y;
lgtData.sliceIndex = m_CubeReflTexArray.FetchSlice(cubemap);
var delta = combinedExtent - e;
lgtData.boxInnerDist = e;
lgtData.boxInvRange.Set(1.0f / delta.x, 1.0f / delta.y, 1.0f / delta.z);
bndData.center = Cw;
bndData.boxAxisX = combinedExtent.x * vx;
bndData.boxAxisY = combinedExtent.y * vy;
bndData.boxAxisZ = combinedExtent.z * vz;
bndData.scaleXY.Set(1.0f, 1.0f);
bndData.radius = combinedExtent.magnitude;
// fill up ldata
lgtData.lightType = (uint)LightDefinitions.BOX_LIGHT;
lgtData.lightModel = (uint)LightDefinitions.REFLECTION_LIGHT;
int i = LightDefinitions.REFLECTION_LIGHT, j = LightDefinitions.BOX_LIGHT;
idxOut = numEntries2nd[i, j] + offsets[i, j]; ++numEntries2nd[i, j];
boundData[idxOut] = bndData;
lightData[idxOut] = lgtData;
}
var numProbesOut = offsets[LightDefinitions.REFLECTION_LIGHT, numVolTypes - 1] + numEntries[LightDefinitions.REFLECTION_LIGHT, numVolTypes - 1];
for (var m = 0; m < numModels; m++)
{
for (var v = 0; v < numVolTypes; v++)
Debug.Assert(numEntries[m, v] == numEntries2nd[m, v], "count mismatch on second pass!");
}
s_ConvexBoundsBuffer.SetData(boundData);
s_LightDataBuffer.SetData(lightData);
return numLightsOut + numProbesOut;
}
void BuildPerTileLightLists(Camera camera, RenderLoop loop, int numLights, Matrix4x4 projscr, Matrix4x4 invProjscr)
{
var w = camera.pixelWidth;
var h = camera.pixelHeight;
var numTilesX = (w + 15) / 16;
var numTilesY = (h + 15) / 16;
var numBigTilesX = (w + 63) / 64;
var numBigTilesY = (h + 63) / 64;
var cmd = new CommandBuffer() { name = "Build light list" };
// generate screen-space AABBs (used for both fptl and clustered).
{
var proj = CameraProjection(camera);
var temp = new Matrix4x4();
temp.SetRow(0, new Vector4(1.0f, 0.0f, 0.0f, 0.0f));
temp.SetRow(1, new Vector4(0.0f, 1.0f, 0.0f, 0.0f));
temp.SetRow(2, new Vector4(0.0f, 0.0f, 0.5f, 0.5f));
temp.SetRow(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
var projh = temp * proj;
var invProjh = projh.inverse;
cmd.SetComputeIntParam(buildScreenAABBShader, "g_iNrVisibLights", numLights);
SetMatrixCS(cmd, buildScreenAABBShader, "g_mProjection", projh);
SetMatrixCS(cmd, buildScreenAABBShader, "g_mInvProjection", invProjh);
cmd.SetComputeBufferParam(buildScreenAABBShader, s_GenAABBKernel, "g_vBoundsBuffer", s_AABBBoundsBuffer);
cmd.DispatchCompute(buildScreenAABBShader, s_GenAABBKernel, (numLights + 7) / 8, 1, 1);
}
// enable coarse 2D pass on 64x64 tiles (used for both fptl and clustered).
if (enableBigTilePrepass)
{
cmd.SetComputeIntParams(buildPerBigTileLightListShader, "g_viDimensions", new int[2] { w, h });
cmd.SetComputeIntParam(buildPerBigTileLightListShader, "g_iNrVisibLights", numLights);
SetMatrixCS(cmd, buildPerBigTileLightListShader, "g_mScrProjection", projscr);
SetMatrixCS(cmd, buildPerBigTileLightListShader, "g_mInvScrProjection", invProjscr);
cmd.SetComputeFloatParam(buildPerBigTileLightListShader, "g_fNearPlane", camera.nearClipPlane);
cmd.SetComputeFloatParam(buildPerBigTileLightListShader, "g_fFarPlane", camera.farClipPlane);
cmd.SetComputeBufferParam(buildPerBigTileLightListShader, s_GenListPerBigTileKernel, "g_vLightList", s_BigTileLightList);
cmd.DispatchCompute(buildPerBigTileLightListShader, s_GenListPerBigTileKernel, numBigTilesX, numBigTilesY, 1);
}
if (usingFptl) // optimized for opaques only
{
cmd.SetComputeIntParams(buildPerTileLightListShader, "g_viDimensions", new int[2] { w, h });
cmd.SetComputeIntParam(buildPerTileLightListShader, "g_iNrVisibLights", numLights);
SetMatrixCS(cmd, buildPerTileLightListShader, "g_mScrProjection", projscr);
SetMatrixCS(cmd, buildPerTileLightListShader, "g_mInvScrProjection", invProjscr);
cmd.SetComputeTextureParam(buildPerTileLightListShader, s_GenListPerTileKernel, "g_depth_tex", new RenderTargetIdentifier(s_CameraDepthTexture));
cmd.SetComputeBufferParam(buildPerTileLightListShader, s_GenListPerTileKernel, "g_vLightList", s_LightList);
if (enableBigTilePrepass) cmd.SetComputeBufferParam(buildPerTileLightListShader, s_GenListPerTileKernel, "g_vBigTileLightList", s_BigTileLightList);
cmd.DispatchCompute(buildPerTileLightListShader, s_GenListPerTileKernel, numTilesX, numTilesY, 1);
}
if (enableClustered) // works for transparencies too.
{
VoxelLightListGeneration(cmd, camera, numLights, projscr, invProjscr);
}
loop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
*/
public void PushGlobalParams(Camera camera, RenderLoop loop, HDRenderLoop.LightList lightList)
{
}
}
}
}

50
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.cs.hlsl


//
// This file was automatically generated from Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.cs. Please don't edit by hand.
//
#ifndef SINGLEPASS_CS_HLSL
#define SINGLEPASS_CS_HLSL
//
// UnityEngine.Experimental.ScriptableRenderLoop.ShadowType: static fields
//
#define SHADOWTYPE_SPOT (0)
#define SHADOWTYPE_DIRECTIONAL (1)
#define SHADOWTYPE_POINT (2)
// Generated from UnityEngine.Experimental.ScriptableRenderLoop.PunctualShadowData
// PackingRules = Exact
struct PunctualShadowData
{
float4x4 worldToShadow;
int shadowType;
float bias;
float quality;
float2 unused;
};
//
// Accessors for UnityEngine.Experimental.ScriptableRenderLoop.PunctualShadowData
//
float4x4 GetWorldToShadow(PunctualShadowData value)
{
return value.worldToShadow;
}
int GetShadowType(PunctualShadowData value)
{
return value.shadowType;
}
float GetBias(PunctualShadowData value)
{
return value.bias;
}
float GetQuality(PunctualShadowData value)
{
return value.quality;
}
float2 GetUnused(PunctualShadowData value)
{
return value.unused;
}
#endif

9
Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/SinglePass/SinglePass.cs.hlsl.meta


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