浏览代码

Merge pull request #1121 from EvgeniiG/master

Add local box volume support to volumetrics
/main
GitHub 7 年前
当前提交
7650c7a6
共有 27 个文件被更改,包括 977 次插入431 次删除
  1. 45
      ScriptableRenderPipeline/Core/CoreRP/GeometryUtils.cs
  2. 18
      ScriptableRenderPipeline/Core/CoreRP/GeometryUtils.cs.hlsl
  3. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/HDAssetFactory.cs
  4. 24
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDRenderPipeline.cs
  5. 4
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDStringConstants.cs
  6. 1
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightEvaluation.hlsl
  7. 90
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoop.cs
  8. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoop.cs.hlsl
  9. 22
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoopDef.hlsl
  10. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/lightlistbuild-bigtile.compute
  11. 12
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/lightlistbuild-clustered.compute
  12. 19
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/lightlistbuild.compute
  13. 10
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/HomogeneousDensityVolume.cs
  14. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/HomogeneousDensityVolume.cs.meta
  15. 324
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs
  16. 10
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs.hlsl
  17. 4
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/RenderPipelineResources/HDRenderPipelineResources.asset
  18. 1
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/RenderPipelineResources/RenderPipelineResources.cs
  19. 1
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderPass/ShaderPass.cs
  20. 5
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderPass/ShaderPass.cs.hlsl
  21. 478
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute
  22. 303
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumeVoxelization.compute
  23. 8
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumeVoxelization.compute.meta
  24. 8
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute.meta
  25. 10
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/Resources.meta
  26. 0
      /ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute

45
ScriptableRenderPipeline/Core/CoreRP/GeometryUtils.cs


using System;
using System;
namespace UnityEngine.Experimental.Rendering
{

[GenerateHLSL]
public struct OrientedBBox
{
public Vector3 center;
public float extentX;
// 3 x float4 = 48 bytes.
// TODO: pack the axes into 16-bit UNORM per channel, and consider a quaternionic representation.
public float extentX;
public Vector3 up;
public Vector3 up;
public Vector3 center;
public Vector3 forward { get { return Vector3.Cross(up, right); } }
Vector3 vecX = t.localToWorldMatrix.GetColumn(0);
Vector3 vecY = t.localToWorldMatrix.GetColumn(1);
Vector3 vecZ = t.localToWorldMatrix.GetColumn(2);
obb.right = t.right;
obb.up = t.up;
obb.extentX = 0.5f * t.localScale.x;
obb.extentY = 0.5f * t.localScale.y;
obb.extentZ = 0.5f * t.localScale.z;
obb.right = vecX * (1.0f / vecX.magnitude);
obb.up = vecY * (1.0f / vecY.magnitude);
obb.extentX = 0.5f * vecX.magnitude;
obb.extentY = 0.5f * vecY.magnitude;
obb.extentZ = 0.5f * vecZ.magnitude;
return obb;
}

{
// Returns 'true' if the OBB intersects (or is inside) the frustum, 'false' otherwise.
// 'cameraRelativeOffset' can be used to intersect a world-space OBB with a camera-relative frustum.
public static bool Overlap(OrientedBBox obb, Vector3 cameraRelativeOffset,
Frustum frustum, int numPlanes, int numCorners)
public static bool Overlap(OrientedBBox obb, Frustum frustum, int numPlanes, int numCorners)
Vector3 center = obb.center + cameraRelativeOffset;
Vector3 forward = Vector3.Cross(obb.up, obb.right);
// Test the OBB against frustum planes. Frustum planes have inward-facing.
// Test the OBB against frustum planes. Frustum planes are inward-facing.
// The OBB is outside if it's entirely behind one of the frustum planes.
// See "Real-Time Rendering", 3rd Edition, 16.10.2.
for (int i = 0; overlap && i < numPlanes; i++)

// Max projection of the half-diagonal onto the normal (always positive).
float maxHalfDiagProj = obb.extentX * Mathf.Abs(Vector3.Dot(n, obb.right))
+ obb.extentY * Mathf.Abs(Vector3.Dot(n, obb.up))
+ obb.extentZ * Mathf.Abs(Vector3.Dot(n, forward));
+ obb.extentZ * Mathf.Abs(Vector3.Dot(n, obb.forward));
// Positive distance -> center in front of the plane.
float centerToPlaneDist = Vector3.Dot(n, center) + d;
float centerToPlaneDist = Vector3.Dot(n, obb.center) + d;
// outside = maxHalfDiagProj < -centerToPlaneDist
// outside = maxHalfDiagProj + centerToPlaneDist < 0

planes[0].distance = obb.extentX;
planes[1].normal = obb.up;
planes[1].distance = obb.extentY;
planes[2].normal = forward;
planes[2].normal = obb.forward;
planes[2].distance = obb.extentZ;
for (int i = 0; overlap && i < 3; i++)

// Merge 2 loops. Continue as long as all points are outside either plane.
for (int j = 0; j < numCorners; j++)
{
float proj = Vector3.Dot(plane.normal, frustum.corners[j] - center);
float proj = Vector3.Dot(plane.normal, frustum.corners[j] - obb.center);
outsidePos = outsidePos && ( proj > plane.distance);
outsideNeg = outsideNeg && (-proj > plane.distance);
}

18
ScriptableRenderPipeline/Core/CoreRP/GeometryUtils.cs.hlsl


// PackingRules = Exact
struct OrientedBBox
{
float3 center;
float3 right;
float3 right;
float3 up;
float3 up;
float3 center;
float extentZ;
};

float3 GetCenter(OrientedBBox value)
float3 GetRight(OrientedBBox value)
return value.center;
return value.right;
float3 GetRight(OrientedBBox value)
float3 GetUp(OrientedBBox value)
return value.right;
return value.up;
float3 GetUp(OrientedBBox value)
float3 GetCenter(OrientedBBox value)
return value.up;
return value.center;
}
float GetExtentZ(OrientedBBox value)
{

3
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/HDAssetFactory.cs


newAsset.deferredComputeShader = Load<ComputeShader>(HDRenderPipelinePath + "Lighting/LightLoop/Deferred.compute");
newAsset.deferredDirectionalShadowComputeShader = Load<ComputeShader>(HDRenderPipelinePath + "Lighting/DeferredDirectionalShadow.compute");
newAsset.volumetricLightingCS = Load<ComputeShader>(HDRenderPipelinePath + "Lighting/Volumetrics/Resources/VolumetricLighting.compute");
newAsset.volumeVoxelizationCS = Load<ComputeShader>(HDRenderPipelinePath + "Lighting/Volumetrics/VolumeVoxelization.compute");
newAsset.volumetricLightingCS = Load<ComputeShader>(HDRenderPipelinePath + "Lighting/Volumetrics/VolumetricLighting.compute");
newAsset.subsurfaceScatteringCS = Load<ComputeShader>(HDRenderPipelinePath + "Material/SubsurfaceScattering/SubsurfaceScattering.compute");
newAsset.subsurfaceScattering = Load<Shader>(HDRenderPipelinePath + "Material/SubsurfaceScattering/SubsurfaceScattering.shader");

24
ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDRenderPipeline.cs


readonly SkyManager m_SkyManager = new SkyManager();
readonly LightLoop m_LightLoop = new LightLoop();
readonly ShadowSettings m_ShadowSettings = new ShadowSettings();
readonly VolumetricLightingModule m_VolumetricLightingModule = new VolumetricLightingModule();
readonly VolumetricLightingSystem m_VolumetricLightingSystem = new VolumetricLightingSystem();
// Debugging
MaterialPropertyBlock m_SharedPropertyBlock = new MaterialPropertyBlock();

m_SkyManager.Build(asset, m_IBLFilterGGX);
m_VolumetricLightingModule.Build(asset);
m_VolumetricLightingSystem.Build(asset);
m_DebugDisplaySettings.RegisterDebug();
#if UNITY_EDITOR

m_SSSBufferManager.Cleanup();
m_SkyManager.Cleanup();
m_VolumetricLightingModule.Cleanup();
m_VolumetricLightingSystem.Cleanup();
m_IBLFilterGGX.Cleanup();
DestroyRenderTextures();

}
// Warning: (resolutionChanged == false) if you open a new Editor tab of the same size!
m_VolumetricLightingModule.ResizeVBuffer(hdCamera, hdCamera.actualWidth, hdCamera.actualHeight);
m_VolumetricLightingSystem.ResizeVBuffer(hdCamera, hdCamera.actualWidth, hdCamera.actualHeight);
// update recorded window resolution
m_CurrentWidth = hdCamera.actualWidth;

m_DbufferManager.PushGlobalParams(cmd, m_FrameSettings);
m_VolumetricLightingModule.PushGlobalParams(hdCamera, cmd);
m_VolumetricLightingSystem.PushGlobalParams(hdCamera, cmd);
}
}

continue;
}
// Frustum cull density volumes on the CPU. Can be performed as soon as the camera is set up.
DensityVolumeList densityVolumes = m_VolumetricLightingSystem.PrepareVisibleDensityVolumeList(hdCamera, cmd);
// Note: Legacy Unity behave like this for ShadowMask
// When you select ShadowMask in Lighting panel it recompile shaders on the fly with the SHADOW_MASK keyword.
// However there is no C# function that we can query to know what mode have been select in Lighting Panel and it will be wrong anyway. Lighting Panel setup what will be the next bake mode. But until light is bake, it is wrong.

bool enableBakeShadowMask;
using (new ProfilingSample(cmd, "TP_PrepareLightsForGPU", CustomSamplerId.TPPrepareLightsForGPU.GetSampler()))
{
enableBakeShadowMask = m_LightLoop.PrepareLightsForGPU(cmd, m_ShadowSettings, m_CullResults, m_ReflectionProbeCullResults, camera) && m_FrameSettings.enableShadowMask;
enableBakeShadowMask = m_LightLoop.PrepareLightsForGPU(cmd, camera, m_ShadowSettings, m_CullResults, m_ReflectionProbeCullResults, densityVolumes) && m_FrameSettings.enableShadowMask;
}
ConfigureForShadowMask(enableBakeShadowMask, cmd);

}
}
// The pass only requires the volume properties, and can run async.
m_VolumetricLightingModule.VoxelizeDensityVolumes(hdCamera, cmd);
// Perform the voxelization step which fills the density 3D texture.
// Requires the clustered lighting data structure to be built, and can run async.
m_VolumetricLightingSystem.VolumeVoxelizationPass(densityVolumes, hdCamera, cmd, m_FrameSettings);
m_VolumetricLightingModule.VolumetricLightingPass(hdCamera, cmd, m_FrameSettings);
m_VolumetricLightingSystem.VolumetricLightingPass(hdCamera, cmd, m_FrameSettings);
RenderDeferredLighting(hdCamera, cmd);

m_SkyManager.RenderSky(hdCamera, m_LightLoop.GetCurrentSunLight(), m_CameraColorBuffer, m_CameraDepthStencilBuffer, cmd);
if (visualEnv.fogType != FogType.None || m_VolumetricLightingModule.preset != VolumetricLightingModule.VolumetricLightingPreset.Off)
if (visualEnv.fogType != FogType.None || m_VolumetricLightingSystem.preset != VolumetricLightingSystem.VolumetricLightingPreset.Off)
m_SkyManager.RenderOpaqueAtmosphericScattering(cmd);
}

4
ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDStringConstants.cs


public static readonly int g_LayeredSingleIdxBuffer = Shader.PropertyToID("g_LayeredSingleIdxBuffer");
public static readonly int _EnvLightIndexShift = Shader.PropertyToID("_EnvLightIndexShift");
public static readonly int _DensityVolumeIndexShift = Shader.PropertyToID("_DensityVolumeIndexShift");
public static readonly int g_isOrthographic = Shader.PropertyToID("g_isOrthographic");
public static readonly int g_iNrVisibLights = Shader.PropertyToID("g_iNrVisibLights");

public static readonly int _VBufferLightingHistory = Shader.PropertyToID("_VBufferLightingHistory");
public static readonly int _VBufferLightingFeedback = Shader.PropertyToID("_VBufferLightingFeedback");
public static readonly int _VBufferSampleOffset = Shader.PropertyToID("_VBufferSampleOffset");
public static readonly int _VolumeBounds = Shader.PropertyToID("_VolumeBounds");
public static readonly int _VolumeProperties = Shader.PropertyToID("_VolumeProperties");
public static readonly int _NumVisibleDensityVolumes = Shader.PropertyToID("_NumVisibleDensityVolumes");
}
}

1
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightEvaluation.hlsl


lightData.angleScale, lightData.angleOffset);
#if (SHADEROPTIONS_VOLUMETRIC_LIGHTING_PRESET != 0)
// TODO: sample the extinction from the density V-buffer.
float distVol = (lightData.lightType == GPULIGHTTYPE_PROJECTOR_BOX) ? distances.w : distances.x;
attenuation *= TransmittanceHomogeneousMedium(_GlobalExtinction, distVol);
#endif

90
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoop.cs


Area,
Env,
Decal,
DensityVolume,
Count
}

EnvironmentAndPunctual = 5,
EnvironmentAndArea = 6,
EnvironmentAndAreaAndPunctual = 7,
Decal = 8
Decal = 8,
DensityVolumes = 16
};
public const int k_MaxDirectionalLightsOnScreen = 4;

int m_punctualLightCount = 0;
int m_areaLightCount = 0;
int m_lightCount = 0;
int m_densityVolumeCount = 0;
bool m_enableBakeShadowMask = false; // Track if any light require shadow mask. In this case we will need to enable the keyword shadow mask
float m_maxShadowDistance = 0.0f; // Save value from shadow settings

}
}
public void AddBoxVolumeDataAndBound(OrientedBBox obb, LightCategory category, LightFeatureFlags featureFlags, Matrix4x4 worldToView)
{
var bound = new SFiniteLightBound();
var volumeData = new LightVolumeData();
// transform to camera space (becomes a left hand coordinate frame in Unity since Determinant(worldToView)<0)
var positionVS = worldToView.MultiplyPoint(obb.center);
var rightVS = worldToView.MultiplyVector(obb.right);
var upVS = worldToView.MultiplyVector(obb.up);
var forwardVS = Vector3.Cross(upVS, rightVS);
var extents = new Vector3(obb.extentX, obb.extentY, obb.extentZ);
volumeData.lightVolume = (uint)LightVolumeType.Box;
volumeData.lightCategory = (uint)category;
volumeData.featureFlags = (uint)featureFlags;
bound.center = positionVS;
bound.boxAxisX = obb.extentX * rightVS;
bound.boxAxisY = obb.extentY * upVS;
bound.boxAxisZ = obb.extentZ * forwardVS;
bound.radius = extents.magnitude;
bound.scaleXY.Set(1.0f, 1.0f);
// The culling system culls pixels that are further
// than a threshold to the box influence extents.
// So we use an arbitrary threshold here (k_BoxCullingExtentOffset)
volumeData.lightPos = positionVS;
volumeData.lightAxisX = rightVS;
volumeData.lightAxisY = upVS;
volumeData.lightAxisZ = forwardVS;
volumeData.boxInnerDist = extents - k_BoxCullingExtentThreshold; // We have no blend range, but the culling code needs a small EPS value for some reason???
volumeData.boxInvRange.Set(1.0f / k_BoxCullingExtentThreshold.x, 1.0f / k_BoxCullingExtentThreshold.y, 1.0f / k_BoxCullingExtentThreshold.z);
m_lightList.bounds.Add(bound);
m_lightList.lightVolumes.Add(volumeData);
}
public int GetCurrentShadowCount()
{
return m_ShadowRequests.Count;

}
// Return true if BakedShadowMask are enabled
public bool PrepareLightsForGPU(CommandBuffer cmd, ShadowSettings shadowSettings, CullResults cullResults, ReflectionProbeCullResults reflectionProbeCullResults, Camera camera)
public bool PrepareLightsForGPU(CommandBuffer cmd, Camera camera, ShadowSettings shadowSettings, CullResults cullResults,
ReflectionProbeCullResults reflectionProbeCullResults, DensityVolumeList densityVolumes)
{
using (new ProfilingSample(cmd, "Prepare Lights For GPU"))
{

if (ShaderConfig.s_CameraRelativeRendering != 0)
{
// Caution: 'DirectionalLightData.positionWS' is camera-relative after this point.
int n = m_lightList.directionalLights.Count;
DirectionalLightData lightData = m_lightList.directionalLights[n - 1];
int last = m_lightList.directionalLights.Count - 1;
DirectionalLightData lightData = m_lightList.directionalLights[last];
m_lightList.directionalLights[n - 1] = lightData;
m_lightList.directionalLights[last] = lightData;
}
}
continue;

if (ShaderConfig.s_CameraRelativeRendering != 0)
{
// Caution: 'LightData.positionWS' is camera-relative after this point.
int n = m_lightList.lights.Count;
LightData lightData = m_lightList.lights[n - 1];
int last = m_lightList.lights.Count - 1;
LightData lightData = m_lightList.lights[last];
m_lightList.lights[n - 1] = lightData;
m_lightList.lights[last] = lightData;
}
}
}

if (ShaderConfig.s_CameraRelativeRendering != 0)
{
// Caution: 'EnvLightData.positionWS' is camera-relative after this point.
int n = m_lightList.envLights.Count;
EnvLightData envLightData = m_lightList.envLights[n - 1];
int last = m_lightList.envLights.Count - 1;
EnvLightData envLightData = m_lightList.envLights[last];
m_lightList.envLights[n - 1] = envLightData;
m_lightList.envLights[last] = envLightData;
// Inject density volumes into the clustered data structure for efficient look up.
m_densityVolumeCount = densityVolumes.bounds != null ? densityVolumes.bounds.Count : 0;
Matrix4x4 worldToViewCR = worldToView;
if (ShaderConfig.s_CameraRelativeRendering != 0)
{
// The OBBs are camera-relative, the matrix is not. Fix it.
worldToViewCR.SetColumn(3, new Vector4(0, 0, 0, 1));
}
for (int i = 0, n = m_densityVolumeCount; i < n; i++)
{
// Density volumes are not lights and therefore should not affect light classification.
LightFeatureFlags featureFlags = 0;
AddBoxVolumeDataAndBound(densityVolumes.bounds[i], LightCategory.DensityVolume, featureFlags, worldToViewCR);
}
m_lightCount = m_lightList.lights.Count + m_lightList.envLights.Count;
Debug.Assert(m_lightList.bounds.Count == m_lightCount);
Debug.Assert(m_lightList.lightVolumes.Count == m_lightCount);
m_lightCount = m_lightList.lights.Count + m_lightList.envLights.Count + m_densityVolumeCount;
Debug.Assert(m_lightCount == m_lightList.bounds.Count);
Debug.Assert(m_lightCount == m_lightList.lightVolumes.Count);
int decalDatasCount = Math.Min(DecalSystem.m_DecalDatasCount, k_MaxDecalsOnScreen);
if (decalDatasCount > 0)

cmd.SetComputeBufferParam(buildPerVoxelLightListShader, s_ClearVoxelAtomicKernel, HDShaderIDs.g_LayeredSingleIdxBuffer, s_GlobalLightListAtomic);
cmd.DispatchCompute(buildPerVoxelLightListShader, s_ClearVoxelAtomicKernel, 1, 1, 1);
int decalDatasCount = Math.Min(DecalSystem.m_DecalDatasCount, k_MaxDecalsOnScreen);
cmd.SetComputeIntParam(buildPerVoxelLightListShader, HDShaderIDs._DensityVolumeIndexShift, m_lightList.lights.Count + m_lightList.envLights.Count + decalDatasCount);
cmd.SetComputeIntParam(buildPerVoxelLightListShader, HDShaderIDs.g_iNrVisibLights, m_lightCount);
cmd.SetComputeMatrixArrayParam(buildPerVoxelLightListShader, HDShaderIDs.g_mScrProjectionArr, projscrArr);
cmd.SetComputeMatrixArrayParam(buildPerVoxelLightListShader, HDShaderIDs.g_mInvScrProjectionArr, invProjscrArr);

3
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoop.cs.hlsl


#define LIGHTCATEGORY_AREA (1)
#define LIGHTCATEGORY_ENV (2)
#define LIGHTCATEGORY_DECAL (3)
#define LIGHTCATEGORY_COUNT (4)
#define LIGHTCATEGORY_DENSITY_VOLUME (4)
#define LIGHTCATEGORY_COUNT (5)
//
// UnityEngine.Experimental.Rendering.HDPipeline.LightFeatureFlags: static fields

22
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/LightLoopDef.hlsl


return 1;
}
uint FetchIndex(uint globalOffset, uint lightIndex)
{
return globalOffset + lightIndex;
}
uint FetchIndexWithBoundsCheck(uint start, uint count, uint i)
{
if (i < count)
{
return FetchIndex(start, i);
}
else
{
return UINT_MAX;
}
}
#ifdef LIGHTLOOP_TILE_PASS
#else
int j = start + i;
#endif
return _LightDatas[j];
}

3
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/lightlistbuild-bigtile.compute


uniform float g_fNearPlane;
uniform float g_fFarPlane;
uniform uint g_isOrthographic;
// TODO: These aren't used, we should remove them
uniform int _EnvLightIndexShift;
uniform int _DecalIndexShift;
StructuredBuffer<float3> g_vBoundsBuffer : register( t1 );
StructuredBuffer<LightVolumeData> _LightVolumeData : register(t2);

12
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/lightlistbuild-clustered.compute


uint g_isOrthographic;
int _EnvLightIndexShift;
int _DecalIndexShift;
int _DensityVolumeIndexShift;
float g_fClustScale;
float g_fClustBase;

int shiftIndex[LIGHTCATEGORY_COUNT];
ZERO_INITIALIZE_ARRAY(int, shiftIndex, LIGHTCATEGORY_COUNT);
// NOTE: Why is this indexed like this?
shiftIndex[LIGHTCATEGORY_COUNT - 2] = _EnvLightIndexShift;
shiftIndex[LIGHTCATEGORY_COUNT - 1] = _DecalIndexShift;
shiftIndex[LIGHTCATEGORY_COUNT - 3] = _EnvLightIndexShift;
shiftIndex[LIGHTCATEGORY_COUNT - 2] = _DecalIndexShift;
shiftIndex[LIGHTCATEGORY_COUNT - 1] = _DensityVolumeIndexShift;
int categoryListCount[LIGHTCATEGORY_COUNT]; // direct light count and reflection lights
int categoryListCount[LIGHTCATEGORY_COUNT]; // number of direct lights, reflection probes, decals and density volumes
ZERO_INITIALIZE_ARRAY(int, categoryListCount, LIGHTCATEGORY_COUNT);
uint offs = start;

const int lightVolIndex = GenerateLightCullDataIndex(coarseList[l], g_iNrVisibLights, eyeIndex);
uint lightCategory = _LightVolumeData[lightVolIndex].lightCategory;
++categoryListCount[lightCategory];
g_vLayeredLightList[offs++] = coarseList[l] - shiftIndex[lightCategory]; // reflection lights will be last since we sorted
}
g_vLayeredLightList[offs++] = coarseList[l] - shiftIndex[lightCategory];
}
}
#if !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_PSSL)

19
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/LightLoop/lightlistbuild.compute


#define MAX_NR_COARSE_ENTRIES 64
#define MAX_NR_PRUNED_ENTRIES 24
#define CATEGORY_LIST_SIZE (LIGHTCATEGORY_COUNT - 1) // Skip density volumes
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

#endif
groupshared int ldsNrLightsFinal;
groupshared int ldsCategoryListCount[LIGHTCATEGORY_COUNT];
groupshared int ldsCategoryListCount[CATEGORY_LIST_SIZE];
#ifdef PERFORM_SPHERICAL_INTERSECTION_TESTS
groupshared uint lightOffsSph;

for(int l=(int) t; l<(int) g_iNrVisibLights; l += NR_THREADS)
{
#endif
// Skip density volumes (lights are sorted by category). TODO: improve data locality
if (_LightVolumeData[l].lightCategory == LIGHTCATEGORY_DENSITY_VOLUME) { break; }
const float3 vMi = g_vBoundsBuffer[l];
const float3 vMa = g_vBoundsBuffer[l+g_iNrVisibLights];

}
#endif
//
if(t<LIGHTCATEGORY_COUNT) ldsCategoryListCount[t]=0;
if(t<CATEGORY_LIST_SIZE) ldsCategoryListCount[t]=0;
#ifdef USE_FEATURE_FLAGS
if(t==0) ldsFeatureFlags=0;
#endif

// All our cull data are in the same list, but at render time envLights are separated so we need to shift the index
// to make it work correctly
int shiftIndex[LIGHTCATEGORY_COUNT];
ZERO_INITIALIZE_ARRAY(int, shiftIndex, LIGHTCATEGORY_COUNT);
shiftIndex[LIGHTCATEGORY_COUNT - 2] = _EnvLightIndexShift;
shiftIndex[LIGHTCATEGORY_COUNT - 1] = _DecalIndexShift;
int shiftIndex[CATEGORY_LIST_SIZE];
ZERO_INITIALIZE_ARRAY(int, shiftIndex, CATEGORY_LIST_SIZE);
shiftIndex[CATEGORY_LIST_SIZE - 2] = _EnvLightIndexShift;
shiftIndex[CATEGORY_LIST_SIZE - 1] = _DecalIndexShift;
for(int category=0; category<LIGHTCATEGORY_COUNT; category++)
for(int category=0; category<CATEGORY_LIST_SIZE; category++)
{
int nrLightsFinal = ldsCategoryListCount[category];
int nrLightsFinalClamped = nrLightsFinal<MAX_NR_PRUNED_ENTRIES ? nrLightsFinal : MAX_NR_PRUNED_ENTRIES;

10
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/HomogeneousDensityVolume.cs


[AddComponentMenu("Rendering/Homogeneous Density Volume", 1100)]
public class HomogeneousDensityVolume : MonoBehaviour
{
public VolumeParameters volumeParameters = new VolumeParameters();
public DensityVolumeParameters parameters = new DensityVolumeParameters();
private void Awake()
{

private void OnValidate()
{
volumeParameters.Constrain();
parameters.Constrain();
if (volumeParameters.IsLocalVolume())
if (parameters.IsLocalVolume())
Gizmos.color = volumeParameters.albedo;
Gizmos.color = parameters.albedo;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
}

foreach (HomogeneousDensityVolume volume in volumes)
{
if (volume.enabled && !volume.volumeParameters.IsLocalVolume())
if (volume.enabled && !volume.parameters.IsLocalVolume())
{
globalVolume = volume;
break;

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/HomogeneousDensityVolume.cs.meta


fileFormatVersion: 2
guid: 1c273c50d71d46a4f98a1d23256a8c63
guid: e1fbb15bf92b84f40a1eb030765b5afe
MonoImporter:
externalObjects: {}
serializedVersion: 2

324
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs


namespace UnityEngine.Experimental.Rendering.HDPipeline
{
[GenerateHLSL]
public struct VolumeProperties
public struct DensityVolumeProperties
public static VolumeProperties GetNeutralVolumeProperties()
public static DensityVolumeProperties GetNeutralProperties()
VolumeProperties properties = new VolumeProperties();
DensityVolumeProperties properties = new DensityVolumeProperties();
properties.scattering = Vector3.zero;
properties.extinction = 0;

} // struct VolumeProperties
[Serializable]
public class VolumeParameters
public class DensityVolumeParameters
public bool isLocal; // Enables voxelization
public Color albedo; // Single scattering albedo [0, 1]
public float meanFreePath; // In meters [1, inf]. Should be chromatic - this is an optimization!
public float asymmetry; // Single global parameter for all volumes. TODO: UX
public bool isLocal; // Enables voxelization
public Color albedo; // Single scattering albedo [0, 1]
public float meanFreePath; // In meters [1, inf]. Should be chromatic - this is an optimization!
public float asymmetry; // Only used if (isLocal == false)
public VolumeParameters()
public DensityVolumeParameters()
{
isLocal = true;
albedo = new Color(0.5f, 0.5f, 0.5f);

asymmetry = Mathf.Clamp(asymmetry, -1.0f, 1.0f);
}
public VolumeProperties GetProperties()
public DensityVolumeProperties GetProperties()
VolumeProperties properties = new VolumeProperties();
DensityVolumeProperties properties = new DensityVolumeProperties();
properties.scattering = GetScatteringCoefficient();
properties.extinction = GetExtinctionCoefficient();

} // class VolumeParameters
public class VolumetricLightingModule
public struct DensityVolumeList
{
public List<OrientedBBox> bounds;
public List<DensityVolumeProperties> properties;
}
public class VolumetricLightingSystem
{
public enum VolumetricLightingPreset
{

}
class VBuffer
{
public long viewID = -1; // -1 is invalid; positive for Game Views, 0 otherwise
public RenderTexture[] lightingRTEX = null;
public RenderTargetIdentifier[] lightingRTID = null;
public RenderTexture densityRTEX = null;
public RenderTargetIdentifier densityRTID = -1; // RenderTargetIdentifier cannot be NULL
const int k_IndexDensity = 0;
const int k_IndexIntegral = 1;
const int k_IndexHistory = 2; // Depends on frame ID
const int k_IndexFeedback = 3; // Depends on frame ID
long m_ViewID = -1; // -1 is invalid; positive for Game Views, 0 otherwise
RenderTexture[] m_Textures = null;
RenderTargetIdentifier[] m_Identifiers = null;
public RenderTargetIdentifier GetDensityBuffer()
{
Debug.Assert(m_ViewID >= 0);
return m_Identifiers[k_IndexDensity];
}
Debug.Assert(viewID >= 0);
return lightingRTID[0];
Debug.Assert(m_ViewID >= 0);
return m_Identifiers[k_IndexIntegral];
Debug.Assert(viewID > 0); // Game View only
return lightingRTID[1 + ((Time.renderedFrameCount + 0) & 1)];
Debug.Assert(m_ViewID > 0); // Game View only
return m_Identifiers[k_IndexHistory + (Time.renderedFrameCount & 1)];
Debug.Assert(viewID > 0); // Game View only
return lightingRTID[1 + ((Time.renderedFrameCount + 1) & 1)];
}
public RenderTargetIdentifier GetDensityBuffer()
{
Debug.Assert(viewID >= 0);
return densityRTID;
Debug.Assert(m_ViewID > 0); // Game View only
return m_Identifiers[k_IndexFeedback - (Time.renderedFrameCount & 1)];
}
public void Create(long viewID, int w, int h, int d)

// Clean up first.
Destroy();
// The required number of buffers depends on the view type.
// Only Game Views need history and feedback buffers.
int n = isGameView ? 3 : 1;
int n = isGameView ? 4 : 2;
this.viewID = viewID;
this.lightingRTEX = new RenderTexture[n];
this.lightingRTID = new RenderTargetIdentifier[n];
m_ViewID = viewID;
m_Textures = new RenderTexture[n];
m_Identifiers = new RenderTargetIdentifier[n];
this.lightingRTEX[i] = new RenderTexture(w, h, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
this.lightingRTEX[i].hideFlags = HideFlags.HideAndDontSave;
this.lightingRTEX[i].filterMode = FilterMode.Trilinear; // Custom
this.lightingRTEX[i].dimension = TextureDimension.Tex3D; // TODO: request the thick 3D tiling layout
this.lightingRTEX[i].volumeDepth = d;
this.lightingRTEX[i].enableRandomWrite = true;
this.lightingRTEX[i].name = CoreUtils.GetRenderTargetAutoName(w, h, RenderTextureFormat.ARGBHalf, String.Format("Volumetric{0}", i));
this.lightingRTEX[i].Create();
this.lightingRTID[i] = new RenderTargetIdentifier(this.lightingRTEX[i]);
m_Textures[i] = new RenderTexture(w, h, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
m_Textures[i].hideFlags = HideFlags.HideAndDontSave;
m_Textures[i].filterMode = FilterMode.Trilinear; // Custom
m_Textures[i].dimension = TextureDimension.Tex3D; // TODO: request the thick 3D tiling layout
m_Textures[i].volumeDepth = d;
m_Textures[i].enableRandomWrite = true;
m_Textures[i].name = CoreUtils.GetRenderTargetAutoName(w, h, RenderTextureFormat.ARGBHalf, String.Format("VBuffer{0}", i));
m_Textures[i].Create();
// TODO: clear the texture. Clearing 3D textures does not appear to work right now.
m_Identifiers[i] = new RenderTargetIdentifier(m_Textures[i]);
if (this.lightingRTEX != null)
if (m_Textures != null)
for (int i = 0, n = this.lightingRTEX.Length; i < n; i++)
for (int i = 0, n = m_Textures.Length; i < n; i++)
if (this.lightingRTEX[i] != null)
if (m_Textures[i] != null)
this.lightingRTEX[i].Release();
m_Textures[i].Release();
this.viewID = -1;
this.lightingRTEX = null;
this.lightingRTID = null;
m_ViewID = -1;
m_Textures = null;
m_Identifiers = null;
}
public void GetResolution(ref int w, ref int h, ref int d)
{
Debug.Assert(m_Textures != null);
Debug.Assert(m_Textures[0] != null);
Debug.Assert(m_Identifiers != null);
w = m_Textures[0].width;
h = m_Textures[0].height;
d = m_Textures[0].volumeDepth;
}
public long GetViewID()
{
return m_ViewID;
}
public bool IsValid()
{
return m_ViewID >= 0 && m_Textures != null && m_Textures[0] != null;
ComputeShader m_VolumeVoxelizationCS = null;
List<VBuffer> m_VBuffers = null;
List<OrientedBBox> m_VisibleVolumes = null;
List<VolumeProperties> m_VisibleVolumeProperties = null;
public const int k_MaxVisibleVolumeCount = 512;
List<VBuffer> m_VBuffers = null;
List<OrientedBBox> m_VisibleVolumeBounds = null;
List<DensityVolumeProperties> m_VisibleVolumeProperties = null;
public const int k_MaxVisibleVolumeCount = 512;
static ComputeBuffer s_VisibleVolumesBuffer = null;
static ComputeBuffer s_VisibleVolumeBoundsBuffer = null;
const float k_LogScale = 0.5f;
const float k_LogScale = 0.5f; // Tweak constant, controls the logarithmic depth distribution
m_VolumeVoxelizationCS = asset.renderPipelineResources.volumeVoxelizationCS;
m_VisibleVolumes = new List<OrientedBBox>();
m_VisibleVolumeProperties = new List<VolumeProperties>();
s_VisibleVolumesBuffer = new ComputeBuffer(k_MaxVisibleVolumeCount, System.Runtime.InteropServices.Marshal.SizeOf(typeof(OrientedBBox)));
s_VisibleVolumePropertiesBuffer = new ComputeBuffer(k_MaxVisibleVolumeCount, System.Runtime.InteropServices.Marshal.SizeOf(typeof(VolumeProperties)));
m_VisibleVolumeBounds = new List<OrientedBBox>();
m_VisibleVolumeProperties = new List<DensityVolumeProperties>();
s_VisibleVolumeBoundsBuffer = new ComputeBuffer(k_MaxVisibleVolumeCount, System.Runtime.InteropServices.Marshal.SizeOf(typeof(OrientedBBox)));
s_VisibleVolumePropertiesBuffer = new ComputeBuffer(k_MaxVisibleVolumeCount, System.Runtime.InteropServices.Marshal.SizeOf(typeof(DensityVolumeProperties)));
}
public void Cleanup()

m_VolumeVoxelizationCS = null;
m_VolumetricLightingCS = null;
for (int i = 0, n = m_VBuffers.Count; i < n; i++)

m_VBuffers = null;
m_VisibleVolumes = null;
m_VisibleVolumeBounds = null;
CoreUtils.SafeRelease(s_VisibleVolumesBuffer);
CoreUtils.SafeRelease(s_VisibleVolumeBoundsBuffer);
CoreUtils.SafeRelease(s_VisibleVolumePropertiesBuffer);
}

if (vBuffer != null)
{
Debug.Assert(vBuffer.lightingRTEX != null);
Debug.Assert(vBuffer.lightingRTEX[0] != null);
Debug.Assert(vBuffer.lightingRTID != null);
int width = 0, height = 0, depth = 0;
vBuffer.GetResolution(ref width, ref height, ref depth);
if (w == vBuffer.lightingRTEX[0].width &&
h == vBuffer.lightingRTEX[0].height &&
d == vBuffer.lightingRTEX[0].volumeDepth)
if (w == width && h == height && d == depth)
{
// Everything matches, nothing to do here.
return;

for (int i = 0; i < n; i++)
{
// Check whether domain reload killed it...
if (viewID == m_VBuffers[i].viewID && m_VBuffers[i].lightingRTEX != null && m_VBuffers[i].lightingRTEX[0] != null)
if (viewID == m_VBuffers[i].GetViewID() && m_VBuffers[i].IsValid())
{
vBuffer = m_VBuffers[i];
}

HomogeneousDensityVolume globalVolume = HomogeneousDensityVolume.GetGlobalHomogeneousDensityVolume();
// TODO: may want to cache these results somewhere.
VolumeProperties globalVolumeProperties = (globalVolume != null) ? globalVolume.volumeParameters.GetProperties()
: VolumeProperties.GetNeutralVolumeProperties();
DensityVolumeProperties globalVolumeProperties = (globalVolume != null) ? globalVolume.parameters.GetProperties()
: DensityVolumeProperties.GetNeutralProperties();
float asymmetry = globalVolume != null ? globalVolume.volumeParameters.asymmetry : 0;
float asymmetry = globalVolume != null ? globalVolume.parameters.asymmetry : 0;
int w = 0, h = 0, d = 0;
ComputeVBufferResolutionAndScale(preset, (int)camera.screenSize.x, (int)camera.screenSize.y, ref w, ref h, ref d);
int w = 0, h = 0, d = 0;
vBuffer.GetResolution(ref w, ref h, ref d);
SetPreconvolvedAmbientLightProbe(cmd, asymmetry);
cmd.SetGlobalVector( HDShaderIDs._VBufferResolution, new Vector4(w, h, 1.0f / w, 1.0f / h));
cmd.SetGlobalVector( HDShaderIDs._VBufferSliceCount, new Vector4(d, 1.0f / d));

}
public void VoxelizeDensityVolumes(HDCamera camera, CommandBuffer cmd)
public DensityVolumeList PrepareVisibleDensityVolumeList(HDCamera camera, CommandBuffer cmd)
if (preset == VolumetricLightingPreset.Off) return;
DensityVolumeList densityVolumes = new DensityVolumeList();
Vector3 camPosition = camera.camera.transform.position;
Vector3 camOffset = Vector3.zero; // World-origin-relative
if (preset == VolumetricLightingPreset.Off) return densityVolumes;
if (ShaderConfig.s_CameraRelativeRendering != 0)
using (new ProfilingSample(cmd, "Prepare Visible Density Volume List"))
camOffset = -camPosition; // Camera-relative
}
Vector3 camPosition = camera.camera.transform.position;
Vector3 camOffset = Vector3.zero; // World-origin-relative
m_VisibleVolumes.Clear();
m_VisibleVolumeProperties.Clear();
if (ShaderConfig.s_CameraRelativeRendering != 0)
{
camOffset = camPosition; // Camera-relative
}
// Collect all the visible volume data, and upload it to the GPU.
HomogeneousDensityVolume[] volumes = Object.FindObjectsOfType(typeof(HomogeneousDensityVolume)) as HomogeneousDensityVolume[];
m_VisibleVolumeBounds.Clear();
m_VisibleVolumeProperties.Clear();
foreach (HomogeneousDensityVolume volume in volumes)
{
// Only test active finite volumes.
if (volume.enabled && volume.volumeParameters.IsLocalVolume())
// Collect all visible finite volume data, and upload it to the GPU.
HomogeneousDensityVolume[] volumes = Object.FindObjectsOfType(typeof(HomogeneousDensityVolume)) as HomogeneousDensityVolume[];
for (int i = 0; i < Math.Min(volumes.Length, k_MaxVisibleVolumeCount); i++)
// TODO: cache these?
var obb = OrientedBBox.Create(volume.transform);
HomogeneousDensityVolume volume = volumes[i];
// Frustum cull on the CPU for now. TODO: do it on the GPU.
if (GeometryUtils.Overlap(obb, camOffset, camera.frustum, 6, 8))
// Only test active finite volumes.
if (volume.enabled && volume.parameters.IsLocalVolume())
var properties = volume.volumeParameters.GetProperties();
var obb = OrientedBBox.Create(volume.transform);
m_VisibleVolumes.Add(obb);
m_VisibleVolumeProperties.Add(properties);
// Handle camera-relative rendering.
obb.center -= camOffset;
// Frustum cull on the CPU for now. TODO: do it on the GPU.
if (GeometryUtils.Overlap(obb, camera.frustum, 6, 8))
{
// TODO: cache these?
var properties = volume.parameters.GetProperties();
m_VisibleVolumeBounds.Add(obb);
m_VisibleVolumeProperties.Add(properties);
}
s_VisibleVolumeBoundsBuffer.SetData(m_VisibleVolumeBounds);
s_VisibleVolumePropertiesBuffer.SetData(m_VisibleVolumeProperties);
// Fill the struct with pointers in order to share the data with the light loop.
densityVolumes.bounds = m_VisibleVolumeBounds;
densityVolumes.properties = m_VisibleVolumeProperties;
return densityVolumes;
}
s_VisibleVolumesBuffer.SetData(m_VisibleVolumes);
s_VisibleVolumePropertiesBuffer.SetData(m_VisibleVolumeProperties);
public void VolumeVoxelizationPass(DensityVolumeList densityVolumes, HDCamera camera, CommandBuffer cmd, FrameSettings settings)
{
if (preset == VolumetricLightingPreset.Off) return;
using (new ProfilingSample(cmd, "Volume Voxelization"))
{
int numVisibleVolumes = m_VisibleVolumeBounds.Count;
if (numVisibleVolumes == 0)
{
// Clear the render target instead of running the shader.
// CoreUtils.SetRenderTarget(cmd, vBuffer.GetDensityBuffer(), ClearFlag.Color, CoreUtils.clearColorAllBlack);
// return;
// Clearing 3D textures does not seem to work!
// Use the workaround by running the full shader with 0 density.
}
VBuffer vBuffer = FindVBuffer(camera.GetViewID());
Debug.Assert(vBuffer != null);
int w = 0, h = 0, d = 0;
vBuffer.GetResolution(ref w, ref h, ref d);
bool enableClustered = settings.lightLoopSettings.enableTileAndCluster;
int kernel = m_VolumeVoxelizationCS.FindKernel(enableClustered ? "VolumeVoxelizationClustered"
: "VolumeVoxelizationBruteforce");
float vFoV = camera.camera.fieldOfView * Mathf.Deg2Rad;
Vector4 resolution = new Vector4(w, h, 1.0f / w, 1.0f / h);
Matrix4x4 transform = HDUtils.ComputePixelCoordToWorldSpaceViewDirectionMatrix(vFoV, resolution, camera.viewMatrix, false);
camera.SetupComputeShader( m_VolumeVoxelizationCS, cmd);
cmd.SetComputeTextureParam(m_VolumeVoxelizationCS, kernel, HDShaderIDs._VBufferDensity, vBuffer.GetDensityBuffer());
cmd.SetComputeBufferParam( m_VolumeVoxelizationCS, kernel, HDShaderIDs._VolumeBounds, s_VisibleVolumeBoundsBuffer);
cmd.SetComputeBufferParam( m_VolumeVoxelizationCS, kernel, HDShaderIDs._VolumeProperties, s_VisibleVolumePropertiesBuffer);
// TODO: set the constant buffer data only once.
cmd.SetComputeMatrixParam( m_VolumeVoxelizationCS, HDShaderIDs._VBufferCoordToViewDirWS, transform);
cmd.SetComputeIntParam( m_VolumeVoxelizationCS, HDShaderIDs._NumVisibleDensityVolumes, numVisibleVolumes);
// The shader defines GROUP_SIZE_1D = 8.
cmd.DispatchCompute(m_VolumeVoxelizationCS, kernel, (w + 7) / 8, (h + 7) / 8, 1);
}
}
// Ref: https://en.wikipedia.org/wiki/Close-packing_of_equal_spheres

return coords;
}
public void VolumetricLightingPass(HDCamera camera, CommandBuffer cmd, FrameSettings frameSettings)
public void VolumetricLightingPass(HDCamera camera, CommandBuffer cmd, FrameSettings settings)
{
if (preset == VolumetricLightingPreset.Off) return;

Debug.Assert(vBuffer != null);
HomogeneousDensityVolume globalVolume = HomogeneousDensityVolume.GetGlobalHomogeneousDensityVolume();
float asymmetry = globalVolume != null ? globalVolume.volumeParameters.asymmetry : 0;
float asymmetry = globalVolume != null ? globalVolume.parameters.asymmetry : 0;
// CoreUtils.SetRenderTarget(cmd, GetVBufferLightingIntegral(viewOffset), ClearFlag.Color, CoreUtils.clearColorAllBlack);
// CoreUtils.SetRenderTarget(cmd, vBuffer.GetLightingIntegralBuffer(), ClearFlag.Color, CoreUtils.clearColorAllBlack);
// CoreUtils.SetRenderTarget(cmd, vBuffer.GetLightingFeedbackBuffer(), ClearFlag.Color, CoreUtils.clearColorAllBlack);
// Use the workaround by running the full shader with no volume.
// Use the workaround by running the full shader with 0 density.
bool enableClustered = frameSettings.lightLoopSettings.enableTileAndCluster;
// Only available in the Play Mode because all the frame counters in the Edit Mode are broken.
bool enableClustered = settings.lightLoopSettings.enableTileAndCluster;
bool enableReprojection = Application.isPlaying && camera.camera.cameraType == CameraType.Game;
int kernel;

// Only available in the Play Mode because all the frame counters in the Edit Mode are broken.
: "VolumetricLightingAllLightsReproj");
: "VolumetricLightingBruteforceReproj");
: "VolumetricLightingAllLights");
: "VolumetricLightingBruteforce");
ComputeVBufferResolutionAndScale(preset, (int)camera.screenSize.x, (int)camera.screenSize.y, ref w, ref h, ref d);
vBuffer.GetResolution(ref w, ref h, ref d);
// Compose the matrix which allows us to compute the world space view direction.
float vFoV = camera.camera.fieldOfView * Mathf.Deg2Rad;

Vector4 offset = new Vector4(xySeq[sampleIndex].x, xySeq[sampleIndex].y, zSeq[sampleIndex], rfc);
// TODO: set 'm_VolumetricLightingPreset'.
cmd.SetComputeFloatParam( m_VolumetricLightingCS, HDShaderIDs._CornetteShanksConstant, CornetteShanksPhasePartConstant(asymmetry));
cmd.SetComputeVectorParam( m_VolumetricLightingCS, HDShaderIDs._VBufferSampleOffset, offset);
// TODO: set the constant buffer data only once.
cmd.SetComputeVectorParam( m_VolumetricLightingCS, HDShaderIDs._VBufferSampleOffset, offset);
cmd.SetComputeFloatParam( m_VolumetricLightingCS, HDShaderIDs._CornetteShanksConstant, CornetteShanksPhasePartConstant(asymmetry));
cmd.SetComputeTextureParam(m_VolumetricLightingCS, kernel, HDShaderIDs._VBufferDensity, vBuffer.GetDensityBuffer()); // Read
cmd.SetComputeTextureParam(m_VolumetricLightingCS, kernel, HDShaderIDs._VBufferLightingIntegral, vBuffer.GetLightingIntegralBuffer()); // Write
if (enableReprojection)
{

// The shader defines GROUP_SIZE_1D = 16.
cmd.DispatchCompute(m_VolumetricLightingCS, kernel, (w + 15) / 16, (h + 15) / 16, 1);
// The shader defines GROUP_SIZE_1D = 8.
cmd.DispatchCompute(m_VolumetricLightingCS, kernel, (w + 7) / 8, (h + 7) / 8, 1);
}
}
} // class VolumetricLightingModule

10
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs.hlsl


#ifndef VOLUMETRICLIGHTING_CS_HLSL
#define VOLUMETRICLIGHTING_CS_HLSL
// Generated from UnityEngine.Experimental.Rendering.HDPipeline.VolumeProperties
// Generated from UnityEngine.Experimental.Rendering.HDPipeline.DensityVolumeProperties
struct VolumeProperties
struct DensityVolumeProperties
{
float3 scattering;
float extinction;

// Accessors for UnityEngine.Experimental.Rendering.HDPipeline.VolumeProperties
// Accessors for UnityEngine.Experimental.Rendering.HDPipeline.DensityVolumeProperties
float3 GetScattering(VolumeProperties value)
float3 GetScattering(DensityVolumeProperties value)
float GetExtinction(VolumeProperties value)
float GetExtinction(DensityVolumeProperties value)
{
return value.extinction;
}

4
ScriptableRenderPipeline/HDRenderPipeline/HDRP/RenderPipelineResources/HDRenderPipelineResources.asset


type: 3}
deferredDirectionalShadowComputeShader: {fileID: 7200000, guid: fbde6fae193b2a94e9fd97c163c204f4,
type: 3}
volumetricLightingCS: {fileID: 7200000, guid: 799166e2ee6a4b041bba9e74f6942097,
volumeVoxelizationCS: {fileID: 7200000, guid: c20b371db720da244b73830ec74a343a,
type: 3}
volumetricLightingCS: {fileID: 7200000, guid: b4901a10df2d1e24282725e9fbc77c97,
type: 3}
subsurfaceScatteringCS: {fileID: 7200000, guid: b06a7993621def248addd55d0fe931b1,
type: 3}

1
ScriptableRenderPipeline/HDRenderPipeline/HDRP/RenderPipelineResources/RenderPipelineResources.cs


public ComputeShader buildMaterialFlagsShader;
public ComputeShader deferredComputeShader;
public ComputeShader deferredDirectionalShadowComputeShader;
public ComputeShader volumeVoxelizationCS;
public ComputeShader volumetricLightingCS;
public ComputeShader subsurfaceScatteringCS; // Disney SSS

1
ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderPass/ShaderPass.cs


LightTransport,
Shadows,
SubsurfaceScattering,
VolumeVoxelization,
VolumetricLighting,
DBuffer
}

5
ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderPass/ShaderPass.cs.hlsl


#define SHADERPASS_LIGHT_TRANSPORT (7)
#define SHADERPASS_SHADOWS (8)
#define SHADERPASS_SUBSURFACE_SCATTERING (9)
#define SHADERPASS_VOLUMETRIC_LIGHTING (10)
#define SHADERPASS_DBUFFER (11)
#define SHADERPASS_VOLUME_VOXELIZATION (10)
#define SHADERPASS_VOLUMETRIC_LIGHTING (11)
#define SHADERPASS_DBUFFER (12)
#endif

478
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute


// Definitions
//--------------------------------------------------------------------------------------------------
#pragma kernel VolumetricLightingAllLights VolumetricLighting=VolumetricLightingAllLights ENABLE_REPROJECTION=0 LIGHTLOOP_SINGLE_PASS
#pragma kernel VolumetricLightingAllLightsReproj VolumetricLighting=VolumetricLightingAllLightsReproj ENABLE_REPROJECTION=1 LIGHTLOOP_SINGLE_PASS
#pragma kernel VolumetricLightingClustered VolumetricLighting=VolumetricLightingClustered ENABLE_REPROJECTION=0 LIGHTLOOP_TILE_PASS USE_CLUSTERED_LIGHTLIST
#pragma kernel VolumetricLightingClusteredReproj VolumetricLighting=VolumetricLightingClusteredReproj ENABLE_REPROJECTION=1 LIGHTLOOP_TILE_PASS USE_CLUSTERED_LIGHTLIST
#pragma kernel VolumetricLightingBruteforce VolumetricLighting=VolumetricLightingBruteforce ENABLE_REPROJECTION=0 LIGHTLOOP_SINGLE_PASS
#pragma kernel VolumetricLightingBruteforceReproj VolumetricLighting=VolumetricLightingBruteforceReproj ENABLE_REPROJECTION=1 LIGHTLOOP_SINGLE_PASS
#pragma kernel VolumetricLightingClustered VolumetricLighting=VolumetricLightingClustered ENABLE_REPROJECTION=0 LIGHTLOOP_TILE_PASS USE_CLUSTERED_LIGHTLIST
#pragma kernel VolumetricLightingClusteredReproj VolumetricLighting=VolumetricLightingClusteredReproj ENABLE_REPROJECTION=1 LIGHTLOOP_TILE_PASS USE_CLUSTERED_LIGHTLIST
#include "../../../ShaderPass/ShaderPass.cs.hlsl"
#include "../../ShaderPass/ShaderPass.cs.hlsl"
#include "../../../ShaderConfig.cs.hlsl"
#include "../../ShaderConfig.cs.hlsl"
// E.g. for 1080p: (1920/8)x(1080/8)x(128) = 4,147,200 voxels
// E.g. for 1080p: (1920/8)x(1080/8)x(64) = 2,073,600 voxels
// E.g. for 1080p: (1920/4)x(1080/4)x(256) = 33,177,600 voxels
// E.g. for 1080p: (1920/4)x(1080/4)x(128) = 16,588,800 voxels
#define VBUFFER_TILE_SIZE 4
#define VBUFFER_SLICE_COUNT 128
#endif

#define GROUP_SIZE_1D 16
#define GROUP_SIZE_2D (GROUP_SIZE_1D * GROUP_SIZE_1D)
#define GROUP_SIZE_1D 8
#if (SHADEROPTIONS_VOLUMETRIC_LIGHTING_PRESET != 0) // Switch between the full and the empty shader

#include "CoreRP/ShaderLibrary/Common.hlsl"
#include "CoreRP/ShaderLibrary/Filtering.hlsl"
#include "CoreRP/ShaderLibrary/VolumeRendering.hlsl"
#include "CoreRP/ShaderLibrary/SpaceFillingCurves.hlsl"
#include "../../../ShaderVariables.hlsl"
#include "../VolumetricLighting.cs.hlsl"
#include "../VBuffer.hlsl"
#include "../../ShaderVariables.hlsl"
#include "VolumetricLighting.cs.hlsl"
#include "VBuffer.hlsl"
#include "../../Lighting.hlsl" // Includes Material.hlsl
#include "../../LightEvaluation.hlsl"
#include "../Lighting.hlsl" // Includes Material.hlsl
#include "../LightEvaluation.hlsl"
#pragma only_renderers d3d11 ps4 xboxone vulkan metal

RW_TEXTURE3D(float4, _VBufferLightingIntegral); // RGB = radiance, A = optical depth
RW_TEXTURE3D(float4, _VBufferLightingFeedback); // RGB = radiance, A = interval length
TEXTURE3D(_VBufferLightingHistory); // RGB = radiance, A = interval length
TEXTURE3D(_VBufferDensity); // RGB = sqrt(scattering), A = sqrt(extinction)
float4 _VBufferSampleOffset; // {x, y, z}, w = rendered frame count
float _CornetteShanksConstant; // CornetteShanksPhasePartConstant(_GlobalAsymmetry)
float4 _VBufferSampleOffset; // Not used by this shader
float _CornetteShanksConstant; // Not used by this shader
uint _NumVisibleDensityVolumes;
CBUFFER_END
//--------------------------------------------------------------------------------------------------

// Multiplication by the scattering coefficient and the phase function is performed outside.
VoxelLighting EvaluateVoxelLighting(LightLoopContext context, uint featureFlags, PositionInputs posInput, float3 centerWS,
DualRay ray, float t0, float t1, float dt, float rndVal, float extinction, float asymmetry
#ifdef LIGHTLOOP_TILE_PASS
, uint clusterIndices[2], float clusterDepths[2])
#ifdef USE_CLUSTERED_LIGHTLIST
, uint lightClusters[2])
#else
)
#endif

return lighting;
#endif
#ifdef LIGHTLOOP_TILE_PASS
// Loop over 1 or 2 light clusters.
int cluster = 0;
do
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL)
float tMin = max(t0, ray.strataDirInvViewZ * clusterDepths[cluster]);
float tMax = t1;
#ifdef USE_CLUSTERED_LIGHTLIST
// Iterate over all lights within 2 (not necessarily unique) clusters overlapping the voxel along Z.
// We need to skip duplicates, but it's not too difficult since lights are sorted by index.
uint lightStarts[2], lightCounts[2];
if (cluster == 0 && (clusterIndices[0] != clusterIndices[1]))
for (uint k = 0; k < 2; k++)
tMax = min(t1, ray.strataDirInvViewZ * clusterDepths[1]);
GetCountAndStartCluster(posInput.tileCoord, lightClusters[k], LIGHTCATEGORY_PUNCTUAL,
lightStarts[k], lightCounts[k]);
#else
float tMin = t0;
float tMax = t1;
#endif // LIGHTLOOP_TILE_PASS
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL)
uint i = 0, j = 0;
if (i < lightCounts[0] || j < lightCounts[1])
uint lightCount, lightStart;
// At least one of the clusters is non-empty.
uint lightIndices[2];
#ifdef LIGHTLOOP_TILE_PASS
GetCountAndStartCluster(posInput.tileCoord, clusterIndices[cluster], LIGHTCATEGORY_PUNCTUAL,
lightStart, lightCount);
#else
lightCount = _PunctualLightCount;
lightStart = 0;
#endif // LIGHTLOOP_TILE_PASS
// Fetch two initial indices from both clusters.
lightIndices[0] = FetchIndexWithBoundsCheck(lightStarts[0], lightCounts[0], i);
lightIndices[1] = FetchIndexWithBoundsCheck(lightStarts[1], lightCounts[1], j);
if (lightCount > 0)
// Process all punctual lights except for box lights (which are technically not even punctual).
do
LightData light = FetchLight(lightStart, 0);
// Process lights in order.
uint lightIndex = min(lightIndices[0], lightIndices[1]);
uint i = 0, last = lightCount - 1;
#else // USE_CLUSTERED_LIGHTLIST
{
uint lightIndex = 0;
// Box lights require special handling (see the next while loop).
while (i <= last && light.lightType != GPULIGHTTYPE_PROJECTOR_BOX)
{
float tEntr = tMin;
float tExit = tMax;
// Process all punctual lights except for box lights (which are technically not even punctual).
for (; lightIndex < _PunctualLightCount; lightIndex++)
{
#endif // USE_CLUSTERED_LIGHTLIST
bool sampleLight = true;
LightData light = _LightDatas[lightIndex];
// Perform ray-cone intersection for pyramid and spot lights.
if (light.lightType != GPULIGHTTYPE_POINT)
{
float lenMul = 1;
// Process box lights in a separate loop.
if (light.lightType == GPULIGHTTYPE_PROJECTOR_BOX) { break; }
if (light.lightType == GPULIGHTTYPE_PROJECTOR_PYRAMID)
{
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU
// s.t. if you were to place them at the distance of 1 directly in front
// of the light, they would give you the "footprint" of the light.
// For spot lights, the cone fit is exact.
// For pyramid lights, however, this is the "inscribed" cone
// (contained within the pyramid), and we want to intersect
// the "escribed" cone (which contains the pyramid).
// Therefore, we have to scale the radii by the sqrt(2).
lenMul = rsqrt(2);
}
float tEntr = t0;
float tExit = t1;
bool sampleLight = true;
float3 coneAxisX = lenMul * light.right;
float3 coneAxisY = lenMul * light.up;
// Perform ray-cone intersection for pyramid and spot lights.
if (light.lightType != GPULIGHTTYPE_POINT)
{
float lenMul = 1;
sampleLight = IntersectRayCone(ray.originWS, ray.strataDirWS,
light.positionWS, light.forward,
coneAxisX, coneAxisY,
tMin, tMax, tEntr, tExit);
if (light.lightType == GPULIGHTTYPE_PROJECTOR_PYRAMID)
{
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU
// s.t. if you were to place them at the distance of 1 directly in front
// of the light, they would give you the "footprint" of the light.
// For spot lights, the cone fit is exact.
// For pyramid lights, however, this is the "inscribed" cone
// (contained within the pyramid), and we want to intersect
// the "escribed" cone (which contains the pyramid).
// Therefore, we have to scale the radii by the sqrt(2).
lenMul = rsqrt(2);
if (sampleLight)
{
// We are unable to adequately sample features larger
// than the half of the length of the integration interval
// divided by the number of temporal samples (7).
// Therefore, we apply this hack to reduce flickering.
float hackMinDistSq = Sq(dt * (0.5 / 7));
float3 coneAxisX = lenMul * light.right;
float3 coneAxisY = lenMul * light.up;
float t, distSq, rcpPdf;
ImportanceSamplePunctualLight(rndVal, light.positionWS,
ray.originWS, ray.strataDirWS,
tEntr, tExit, t, distSq, rcpPdf,
hackMinDistSq);
sampleLight = IntersectRayCone(ray.originWS, ray.strataDirWS,
light.positionWS, light.forward,
coneAxisX, coneAxisY,
t0, t1, tEntr, tExit);
}
posInput.positionWS = GetPointAtDistance(ray, t);
if (sampleLight)
{
// We are unable to adequately sample features larger
// than the half of the length of the integration interval
// divided by the number of temporal samples (7).
// Therefore, we apply this hack to reduce flickering.
float hackMinDistSq = Sq(dt * (0.5 / 7));
float3 lightToSample = posInput.positionWS - light.positionWS;
float distRcp = rsqrt(distSq);
float dist = distSq * distRcp;
float distProj = dot(lightToSample, light.forward);
float4 distances = float4(dist, distSq, distRcp, distProj);
float3 L = -lightToSample * distRcp;
float t, distSq, rcpPdf;
ImportanceSamplePunctualLight(rndVal, light.positionWS,
ray.originWS, ray.strataDirWS,
tEntr, tExit, t, distSq, rcpPdf,
hackMinDistSq);
posInput.positionWS = GetPointAtDistance(ray, t);
float3 color; float attenuation;
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, lightToSample,
distances, color, attenuation);
float3 lightToSample = posInput.positionWS - light.positionWS;
float distRcp = rsqrt(distSq);
float dist = distSq * distRcp;
float distProj = dot(lightToSample, light.forward);
float4 distances = float4(dist, distSq, distRcp, distProj);
float3 L = -lightToSample * distRcp;
// Important:
// Ideally, all scattering calculations should use the stratified versions
// of the sample position and the ray direction. However, correct reprojection
// of asymmetrically scattered lighting (affected by an anisotropic phase
// function) is not possible. We work around this issue by reprojecting
// lighting not affected by the phase function. This basically removes
// the phase function from the temporal integration process. It is a hack.
// The downside is that asymmetry no longer benefits from temporal averaging,
// and any temporal instability of asymmetry causes causes visible jitter.
// In order to stabilize the image, we use the voxel center for all
// asymmetry-related calculations.
float3 centerL = light.positionWS - centerWS;
float cosTheta = dot(centerL, ray.centerDirWS) * rsqrt(dot(centerL, centerL));
float phase = CornetteShanksPhasePartVarying(asymmetry, cosTheta);
float3 color; float attenuation;
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, lightToSample,
distances, color, attenuation);
float intensity = attenuation * rcpPdf;
// Important:
// Ideally, all scattering calculations should use the stratified versions
// of the sample position and the ray direction. However, correct reprojection
// of asymmetrically scattered lighting (affected by an anisotropic phase
// function) is not possible. We work around this issue by reprojecting
// lighting not affected by the phase function. This basically removes
// the phase function from the temporal integration process. It is a hack.
// The downside is that asymmetry no longer benefits from temporal averaging,
// and any temporal instability of asymmetry causes causes visible jitter.
// In order to stabilize the image, we use the voxel center for all
// asymmetry-related calculations.
float3 centerL = light.positionWS - centerWS;
float cosTheta = dot(centerL, ray.centerDirWS) * rsqrt(dot(centerL, centerL));
float phase = CornetteShanksPhasePartVarying(asymmetry, cosTheta);
// Compute transmittance from 't0' to 't'.
intensity *= TransmittanceHomogeneousMedium(extinction, t - t0);
float intensity = attenuation * rcpPdf;
// Compute the amount of in-scattered radiance.
lighting.radianceNoPhase += intensity * color;
lighting.radianceComplete += phase * intensity * color;
}
// Compute transmittance from 't0' to 't'.
intensity *= TransmittanceHomogeneousMedium(extinction, t - t0);
light = FetchLight(lightStart, min(++i, last));
// Compute the amount of in-scattered radiance.
lighting.radianceNoPhase += intensity * color;
lighting.radianceComplete += phase * intensity * color;
while (i <= last) // GPULIGHTTYPE_PROJECTOR_BOX
#ifndef USE_CLUSTERED_LIGHTLIST
}
// Process all box lights.
for (; lightIndex < _PunctualLightCount; lightIndex++)
{
#else // USE_CLUSTERED_LIGHTLIST
// Advance to the next light in one (or both at the same time) clusters.
if (lightIndex == lightIndices[0])
light = FetchLight(lightStart, min(++i, last));
light.lightType = GPULIGHTTYPE_PROJECTOR_BOX;
i++;
lightIndices[0] = FetchIndexWithBoundsCheck(lightStarts[0], lightCounts[0], i);
}
// Convert the box light from OBB to AABB.
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU by (2/w) and (2/h).
float3x3 rotMat = float3x3(light.right, light.up, light.forward);
if (lightIndex == lightIndices[1])
{
j++;
lightIndices[1] = FetchIndexWithBoundsCheck(lightStarts[1], lightCounts[1], j);
}
} while (i < lightCounts[0] || j < lightCounts[1]);
float3 o = mul(rotMat, ray.originWS - light.positionWS);
float3 d = mul(rotMat, ray.strataDirWS);
// Process all box lights.
while (i < lightCounts[0] || j < lightCounts[1])
{
// Process lights in order.
uint lightIndex = min(lightIndices[0], lightIndices[1]);
float range = light.size.x;
float3 boxPt0 = float3(-1, -1, 0);
float3 boxPt1 = float3( 1, 1, range);
#endif // USE_CLUSTERED_LIGHTLIST
float tEntr, tExit;
LightData light = _LightDatas[lightIndex];
light.lightType = GPULIGHTTYPE_PROJECTOR_BOX;
if (IntersectRayAABB(o, d, boxPt0, boxPt1, tMin, tMax, tEntr, tExit))
{
float tOffset, weight;
ImportanceSampleHomogeneousMedium(rndVal, extinction, tExit - tEntr, tOffset, weight);
// Convert the box light from OBB to AABB.
// 'light.right' and 'light.up' vectors are pre-scaled on the CPU by (2/w) and (2/h).
float3x3 rotMat = float3x3(light.right, light.up, light.forward);
float t = tEntr + tOffset;
posInput.positionWS = GetPointAtDistance(ray, t);
float3 o = mul(rotMat, ray.originWS - light.positionWS);
float3 d = mul(rotMat, ray.strataDirWS);
float3 L = -light.forward;
float3 lightToSample = posInput.positionWS - light.positionWS;
float distProj = dot(lightToSample, light.forward);
float4 distances = float4(1, 1, 1, distProj);
float range = light.size.x;
float3 boxPt0 = float3(-1, -1, 0);
float3 boxPt1 = float3( 1, 1, range);
float3 color; float attenuation;
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, lightToSample,
distances, color, attenuation);
float tEntr, tExit;
// Important:
// Ideally, all scattering calculations should use the stratified versions
// of the sample position and the ray direction. However, correct reprojection
// of asymmetrically scattered lighting (affected by an anisotropic phase
// function) is not possible. We work around this issue by reprojecting
// lighting not affected by the phase function. This basically removes
// the phase function from the temporal integration process. It is a hack.
// The downside is that asymmetry no longer benefits from temporal averaging,
// and any temporal instability of asymmetry causes causes visible jitter.
// In order to stabilize the image, we use the voxel center for all
// asymmetry-related calculations.
float3 centerL = light.positionWS - centerWS;
float cosTheta = dot(centerL, ray.centerDirWS) * rsqrt(dot(centerL, centerL));
float phase = CornetteShanksPhasePartVarying(asymmetry, cosTheta);
if (IntersectRayAABB(o, d, boxPt0, boxPt1, t0, t1, tEntr, tExit))
{
float tOffset, weight;
ImportanceSampleHomogeneousMedium(rndVal, extinction, tExit - tEntr, tOffset, weight);
// Note: the 'weight' accounts for transmittance from 'tEntr' to 't'.
float intensity = attenuation * weight;
float t = tEntr + tOffset;
posInput.positionWS = GetPointAtDistance(ray, t);
float3 L = -light.forward;
float3 lightToSample = posInput.positionWS - light.positionWS;
float distProj = dot(lightToSample, light.forward);
float4 distances = float4(1, 1, 1, distProj);
float3 color; float attenuation;
EvaluateLight_Punctual(context, posInput, light, unused, 0, L, lightToSample,
distances, color, attenuation);
// Compute transmittance from 't0' to 'tEntr'.
intensity *= TransmittanceHomogeneousMedium(extinction, tEntr - t0);
// Important:
// Ideally, all scattering calculations should use the stratified versions
// of the sample position and the ray direction. However, correct reprojection
// of asymmetrically scattered lighting (affected by an anisotropic phase
// function) is not possible. We work around this issue by reprojecting
// lighting not affected by the phase function. This basically removes
// the phase function from the temporal integration process. It is a hack.
// The downside is that asymmetry no longer benefits from temporal averaging,
// and any temporal instability of asymmetry causes causes visible jitter.
// In order to stabilize the image, we use the voxel center for all
// asymmetry-related calculations.
float3 centerL = light.positionWS - centerWS;
float cosTheta = dot(centerL, ray.centerDirWS) * rsqrt(dot(centerL, centerL));
float phase = CornetteShanksPhasePartVarying(asymmetry, cosTheta);
// Note: the 'weight' accounts for transmittance from 'tEntr' to 't'.
float intensity = attenuation * weight;
// Compute transmittance from 't0' to 'tEntr'.
intensity *= TransmittanceHomogeneousMedium(extinction, tEntr - t0);
// Compute the amount of in-scattered radiance.
lighting.radianceNoPhase += intensity * color;
lighting.radianceComplete += phase * intensity * color;
}
#ifdef USE_CLUSTERED_LIGHTLIST
// Advance to the next light in one (or both at the same time) clusters.
if (lightIndex == lightIndices[0])
{
i++;
lightIndices[0] = FetchIndexWithBoundsCheck(lightStarts[0], lightCounts[0], i);
}
// Compute the amount of in-scattered radiance.
lighting.radianceNoPhase += intensity * color;
lighting.radianceComplete += phase * intensity * color;
}
if (lightIndex == lightIndices[1])
{
j++;
lightIndices[1] = FetchIndexWithBoundsCheck(lightStarts[1], lightCounts[1], j);
#endif // USE_CLUSTERED_LIGHTLIST
#ifdef LIGHTLOOP_TILE_PASS
cluster++;
// Check whether the voxel is completely inside the light cluster.
} while ((cluster < 2) && (clusterIndices[0] != clusterIndices[1]));
#endif // LIGHTLOOP_TILE_PASS
}
return lighting;
}

// The contribution of the ambient probe does not depend on the position,
// only on the direction and the length of the interval.
// SampleSH9() evaluates the 3-band SH in a given direction.
// The probe is already pre-convolved with the phase function.
#ifdef LIGHTLOOP_TILE_PASS
// Our voxel is not necessarily completely inside a single light cluster.
// Note that Z-binning can solve this problem, as we can iterate over all Z-bins
// to compute min/max light indices, and then use this range for the entire slice.
uint clusterIndices[2];
float clusterDepths[2];
clusterIndices[0] = GetLightClusterIndex(posInput.tileCoord, z0);
clusterDepths[0] = GetLightClusterMinLinearDepth(posInput.tileCoord, clusterIndices[0]);
#endif // LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
// The voxel can overlap up to 2 light clusters along Z, so we have to iterate over both.
// TODO: implement Z-binning which makes Z-range queries easy.
uint lightClusters[2];
lightClusters[0] = GetLightClusterIndex(posInput.tileCoord, z0);
#endif // USE_CLUSTERED_LIGHTLIST
#if defined(SHADER_API_METAL)
[fastopt]

for (uint slice = 0; slice < sliceCountHack; slice++)
#endif
{
uint3 voxelCoord = uint3(posInput.positionSS, slice);
// Warning: this compiles, but it's nonsense. Use DecodeLogarithmicDepthGeneralized().
float z1 = DecodeLogarithmicDepth(e1, _VBufferDepthDecodingParams);
#else
float z1 = DecodeLogarithmicDepthGeneralized(e1, _VBufferDepthDecodingParams);

#ifdef LIGHTLOOP_TILE_PASS
clusterIndices[1] = GetLightClusterIndex(posInput.tileCoord, z1);
clusterDepths[1] = GetLightClusterMinLinearDepth(posInput.tileCoord, clusterIndices[1]);
#ifdef USE_CLUSTERED_LIGHTLIST
lightClusters[1] = GetLightClusterIndex(posInput.tileCoord, z1);
#endif
// Compute the -exact- position of the center of the voxel.

// Sample the participating medium at 'tc' (or 'centerWS').
// We consider it to be constant along the interval [t0, t1] (within the voxel).
// TODO: piecewise linear.
float3 scattering = _GlobalScattering;
float extinction = max(_GlobalExtinction, FLT_MIN); // Avoid NaNs
float3 scattering = LOAD_TEXTURE3D(_VBufferDensity, voxelCoord).rgb;
float extinction = LOAD_TEXTURE3D(_VBufferDensity, voxelCoord).a;
// TODO: define a function ComputeGlobalFogCoefficients(float3 centerWS),
// which allows procedural definition of extinction and scattering.
// Prevent division by 0.
extinction = max(extinction, FLT_MIN);
#if ENABLE_REPROJECTION
// This is a sequence of 7 equidistant numbers from 1/14 to 13/14.

VoxelLighting lighting = EvaluateVoxelLighting(context, featureFlags, posInput, centerWS,
ray, t0, t1, dt, rndVal, extinction, asymmetry
#ifdef LIGHTLOOP_TILE_PASS
, clusterIndices, clusterDepths);
#ifdef USE_CLUSTERED_LIGHTLIST
, lightClusters);
#else
);
#endif

// do not support reprojection and should neither read nor write to the history buffer.
// to the history buffer. This will cause them to alias, but it is the only way
// to prevent ghosting.
_VBufferLightingFeedback[uint3(posInput.positionSS, slice)] = float4(blendedRadiance, dt);
_VBufferLightingFeedback[voxelCoord] = float4(blendedRadiance, dt);
#if SUPPORT_ASYMMETRY
// Extrapolate the influence of the phase function on the results of the current frame.

#endif // SUPPORT_ASYMMETRY
#endif
#else // ENABLE_REPROJECTION
#else // ENABLE_REPROJECTION
#else // SUPPORT_ASYMMETRY
#else
#endif // SUPPORT_ASYMMETRY
#endif
#endif // ENABLE_REPROJECTION

float phase = IsotropicPhaseFunction();
#endif
float4 integral = float4(totalRadiance, opticalDepth);
// Integrate the contribution of the probe over the interval.
// Integral{a, b}{Transmittance(0, t) * L_s(t) dt} = Transmittance(0, a) * Integral{a, b}{Transmittance(0, t - a) * L_s(t) dt}.
float3 probeRadiance = probeInScatteredRadiance * TransmittanceIntegralHomogeneousMedium(extinction, dt);

opticalDepth += 0.5 * extinction * dt;
// Store the voxel data.
_VBufferLightingIntegral[uint3(posInput.positionSS, slice)] = float4(totalRadiance, opticalDepth);
_VBufferLightingIntegral[voxelCoord] = float4(totalRadiance, opticalDepth);
z0 = z1;
#ifdef LIGHTLOOP_TILE_PASS
clusterIndices[0] = clusterIndices[1];
clusterDepths[0] = clusterDepths[1];
#endif // LIGHTLOOP_TILE_PASS
#ifdef USE_CLUSTERED_LIGHTLIST
lightClusters[0] = lightClusters[1];
#endif
[numthreads(GROUP_SIZE_2D, 1, 1)]
[numthreads(GROUP_SIZE_1D, GROUP_SIZE_1D, 1)]
uint groupThreadId : SV_GroupThreadID)
uint2 groupThreadId : SV_GroupThreadID)
// Note: any factor of 64 is a suitable wave size for our algorithm.
uint waveIndex = WaveReadFirstLane(groupThreadId / 64);
uint laneIndex = groupThreadId % 64;
uint quadIndex = laneIndex / 4;
// Arrange threads in the Morton order to optimally match the memory layout of GCN tiles.
uint2 groupCoord = DecodeMorton2D(groupThreadId);
uint2 voxelCoord = groupOffset + groupCoord;
uint2 voxelCoord = groupOffset + groupThreadId;
uint2 tileCoord = voxelCoord * VBUFFER_TILE_SIZE / TILE_SIZE_CLUSTERED;
uint voxelsPerClusterTile = Sq((uint)(TILE_SIZE_CLUSTERED / VBUFFER_TILE_SIZE));

// TODO: this is a compile-time test, make sure the compiler actually scalarizes.
tileCoord = WaveReadFirstLane(tileCoord);
tileCoord = groupOffset * VBUFFER_TILE_SIZE / TILE_SIZE_CLUSTERED;
UNITY_BRANCH if (voxelCoord.x >= (uint)_VBufferResolution.x ||
voxelCoord.y >= (uint)_VBufferResolution.y)
UNITY_BRANCH
if (voxelCoord.x >= (uint)_VBufferResolution.x ||
voxelCoord.y >= (uint)_VBufferResolution.y)
float2 centerCoord = voxelCoord + 0.5;
float2 centerCoord = voxelCoord + float2(0.5, 0.5);
#if ENABLE_REPROJECTION
float2 strataCoord = centerCoord + _VBufferSampleOffset.xy;
#else

// TODO: avoid 2x matrix multiplications by precomputing the world-space offset on the Z=1 plane.
// Compute the (tile-centered) ray direction s.t. its ViewSpace(rayDirWS).z = 1.
float3 centerDirWS = mul(-float3(centerCoord, 1), (float3x3)_VBufferCoordToViewDirWS);
float centerDirLenSq = dot(centerDirWS, centerDirWS);
float centerDirLenRcp = rsqrt(centerDirLenSq);
float centerDirLen = centerDirLenSq * centerDirLenRcp;
// Compute the (tile-centered) ray direction s.t. its ViewSpace(rayDirWS).z = 1.
float3 centerDirWS = mul(-float3(centerCoord, 1), (float3x3)_VBufferCoordToViewDirWS);
float centerDirLenSq = dot(centerDirWS, centerDirWS);
float centerDirLenRcp = rsqrt(centerDirLenSq);
float centerDirLen = centerDirLenSq * centerDirLenRcp;
DualRay ray;

#else
[numthreads(GROUP_SIZE_2D, 1, 1)]
[numthreads(GROUP_SIZE_1D, GROUP_SIZE_1D, 1)]
uint groupThreadId : SV_GroupThreadID)
uint2 groupThreadId : SV_GroupThreadID)
{
// Reduce compile times if the feature is disabled.
}

303
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumeVoxelization.compute


//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
#pragma kernel VolumeVoxelizationBruteforce VolumeVoxelization=VolumeVoxelizationBruteforce LIGHTLOOP_SINGLE_PASS
#pragma kernel VolumeVoxelizationClustered VolumeVoxelization=VolumeVoxelizationClustered LIGHTLOOP_TILE_PASS USE_CLUSTERED_LIGHTLIST
// #pragma enable_d3d11_debug_symbols
#include "../../ShaderPass/ShaderPass.cs.hlsl"
#define SHADERPASS SHADERPASS_VOLUME_VOXELIZATION
#include "../../ShaderConfig.cs.hlsl"
#if (SHADEROPTIONS_VOLUMETRIC_LIGHTING_PRESET == 1)
// E.g. for 1080p: (1920/8)x(1080/8)x(64) = 2,073,600 voxels
#define VBUFFER_TILE_SIZE 8
#define VBUFFER_SLICE_COUNT 64
#else
// E.g. for 1080p: (1920/4)x(1080/4)x(128) = 16,588,800 voxels
#define VBUFFER_TILE_SIZE 4
#define VBUFFER_SLICE_COUNT 128
#endif
#define GROUP_SIZE_1D 8
#define SOFT_VOXELIZATION 1 // Hack which attempts to determine the partial coverage of the voxel
//--------------------------------------------------------------------------------------------------
// Included headers
//--------------------------------------------------------------------------------------------------
#include "CoreRP/ShaderLibrary/Common.hlsl"
#include "CoreRP/GeometryUtils.cs.hlsl"
#include "../../ShaderVariables.hlsl"
#include "VolumetricLighting.cs.hlsl"
#define UNITY_MATERIAL_VOLUMETRIC // Define before including Lighting.hlsl and Material.hlsl
#include "../Lighting.hlsl" // Includes Material.hlsl
#pragma only_renderers d3d11 ps4 xboxone vulkan metal
//--------------------------------------------------------------------------------------------------
// Inputs & outputs
//--------------------------------------------------------------------------------------------------
StructuredBuffer<OrientedBBox> _VolumeBounds;
StructuredBuffer<DensityVolumeProperties> _VolumeProperties;
RW_TEXTURE3D(float4, _VBufferDensity); // RGB = sqrt(scattering), A = sqrt(extinction)
// TODO: avoid creating another Constant Buffer...
CBUFFER_START(UnityVolumetricLighting)
float4x4 _VBufferCoordToViewDirWS; // Actually just 3x3, but Unity can only set 4x4
float4 _VBufferSampleOffset; // Not used by this shader
float _CornetteShanksConstant; // Not used by this shader
uint _NumVisibleDensityVolumes;
CBUFFER_END
//--------------------------------------------------------------------------------------------------
// Implementation
//--------------------------------------------------------------------------------------------------
void FillVolumetricDensityBuffer(PositionInputs posInput, float3 rayOriginWS, float3 rayUnDirWS,
float3 voxelAxisRight, float3 voxelAxisUp, float3 voxelAxisForward)
{
float n = _VBufferDepthDecodingParams.x + _VBufferDepthDecodingParams.z;
float z0 = n; // Start the computation from the near plane
float de = rcp(VBUFFER_SLICE_COUNT); // Log-encoded distance between slices
#ifdef USE_CLUSTERED_LIGHTLIST
// The voxel can overlap up to 2 light clusters along Z, so we have to iterate over both.
// TODO: implement Z-binning which makes Z-range queries easy.
uint volumeStarts[2], volumeCounts[2];
GetCountAndStartCluster(posInput.tileCoord, GetLightClusterIndex(posInput.tileCoord, z0),
LIGHTCATEGORY_DENSITY_VOLUME, volumeStarts[0], volumeCounts[0]);
#endif // USE_CLUSTERED_LIGHTLIST
#if defined(SHADER_API_METAL)
[fastopt]
for (uint slice = 0; slice < VBUFFER_SLICE_COUNT; slice++)
#else
uint sliceCountHack = max(VBUFFER_SLICE_COUNT, (uint)_VBufferDepthEncodingParams.w); // Prevent unrolling...
// TODO: replace 'sliceCountHack' with VBUFFER_SLICE_COUNT when the shader compiler bug is fixed.
for (uint slice = 0; slice < sliceCountHack; slice++)
#endif
{
uint3 voxelCoord = uint3(posInput.positionSS, slice);
float e1 = slice * de + de; // (slice + 1) / sliceCount
#if defined(SHADER_API_METAL)
// Warning: this compiles, but it's nonsense. Use DecodeLogarithmicDepthGeneralized().
float z1 = DecodeLogarithmicDepth(e1, _VBufferDepthDecodingParams);
#else
float z1 = DecodeLogarithmicDepthGeneralized(e1, _VBufferDepthDecodingParams);
#endif
float halfDZ = 0.5 * (z1 - z0);
float z = z0 + halfDZ;
float3 voxelCenterWS = rayOriginWS + z * rayUnDirWS; // Works due to the length of of the dir
// TODO: define a function ComputeGlobalFogCoefficients(float3 voxelCenterWS),
// which allows procedural definition of extinction and scattering.
float3 voxelScattering = _GlobalScattering;
float voxelExtinction = _GlobalExtinction;
#ifdef USE_CLUSTERED_LIGHTLIST
GetCountAndStartCluster(posInput.tileCoord, GetLightClusterIndex(posInput.tileCoord, z1),
LIGHTCATEGORY_DENSITY_VOLUME, volumeStarts[1], volumeCounts[1]);
// Iterate over all volumes within 2 (not necessarily unique) clusters overlapping the voxel along Z.
// We need to skip duplicates, but it's not too difficult since volumes are sorted by index.
uint i = 0, j = 0;
if (i < volumeCounts[0] || j < volumeCounts[1])
{
// At least one of the clusters is non-empty.
uint volumeIndices[2];
// Fetch two initial indices from both clusters.
volumeIndices[0] = FetchIndexWithBoundsCheck(volumeStarts[0], volumeCounts[0], i);
volumeIndices[1] = FetchIndexWithBoundsCheck(volumeStarts[1], volumeCounts[1], j);
do
{
// Process volumes in order.
uint volumeIndex = min(volumeIndices[0], volumeIndices[1]);
#else // USE_CLUSTERED_LIGHTLIST
{
for (uint volumeIndex = 0; volumeIndex < _NumVisibleDensityVolumes; volumeIndex++)
{
#endif // USE_CLUSTERED_LIGHTLIST
OrientedBBox obb = _VolumeBounds[volumeIndex];
float3x3 obbFrame = float3x3(obb.right, obb.up, cross(obb.up, obb.right));
float3 obbExtents = float3(obb.extentX, obb.extentY, obb.extentZ);
// Express the voxel center in the local coordinate system of the box.
float3 voxelCenterBS = mul(voxelCenterWS - obb.center, transpose(obbFrame));
float3 voxelCenterUV = voxelCenterBS / obbExtents;
#if SOFT_VOXELIZATION
// We need to determine which is the face closest to 'voxelCenterBS'.
float minFaceDist = abs(obbExtents.x - abs(voxelCenterBS.x));
// TODO: use v_cubeid_f32.
uint axisIndex; float faceDist;
faceDist = abs(obbExtents.y - abs(voxelCenterBS.y));
axisIndex = (faceDist < minFaceDist) ? 1 : 0;
minFaceDist = min(faceDist, minFaceDist);
faceDist = abs(obbExtents.z - abs(voxelCenterBS.z));
axisIndex = (faceDist < minFaceDist) ? 2 : axisIndex;
float3 N = float3(axisIndex == 0 ? 1 : 0, axisIndex == 1 ? 1 : 0, axisIndex == 2 ? 1 : 0);
// We have determined the normal of the closest face.
// We now have to construct the diagonal of the voxel with the longest extent along this normal.
float3 minDiagPointBS, maxDiagPointBS;
float3 voxelAxisRightBS = mul(voxelAxisRight, transpose(obbFrame));
float3 voxelAxisUpBS = mul(voxelAxisUp, transpose(obbFrame));
float3 voxelAxisForwardBS = mul(voxelAxisForward, transpose(obbFrame));
// Start at the center of the voxel.
minDiagPointBS = maxDiagPointBS = voxelCenterBS;
bool normalFwd = dot(voxelAxisForwardBS, N) >= 0;
float mulForward = normalFwd ? halfDZ : -halfDZ;
float mulMin = normalFwd ? z0 : z1;
float mulMax = normalFwd ? z1 : z0;
minDiagPointBS -= mulForward * voxelAxisForwardBS;
maxDiagPointBS += mulForward * voxelAxisForwardBS;
float mulUp = dot(voxelAxisUpBS, N) >= 0 ? 1 : -1;
minDiagPointBS -= (mulMin * mulUp) * voxelAxisUpBS;
maxDiagPointBS += (mulMax * mulUp) * voxelAxisUpBS;
float mulRight = dot(voxelAxisRightBS, N) >= 0 ? 1 : -1;
minDiagPointBS -= (mulMin * mulRight) * voxelAxisRightBS;
maxDiagPointBS += (mulMax * mulRight) * voxelAxisRightBS;
// We want to determine the fractional overlap of the diagonal and the box.
float3 diagOriginBS = minDiagPointBS;
float3 diagUnDirBS = maxDiagPointBS - minDiagPointBS;
float tEntr, tExit;
IntersectRayAABB(diagOriginBS, diagUnDirBS,
-obbExtents, obbExtents,
0, 1,
tEntr, tExit);
float overlapFraction = tExit - tEntr;
#else // SOFT_VOXELIZATION
bool overlap = abs(voxelCenterUV.x) <= 1 &&
abs(voxelCenterUV.y) <= 1 &&
abs(voxelCenterUV.z) <= 1;
float overlapFraction = overlap ? 1 : 0;
#endif // SOFT_VOXELIZATION
if (overlapFraction > 0)
{
// There is an overlap. Sample the 3D texture, or load the constant value.
voxelScattering += overlapFraction * _VolumeProperties[volumeIndex].scattering;
voxelExtinction += overlapFraction * _VolumeProperties[volumeIndex].extinction;
}
#ifndef USE_CLUSTERED_LIGHTLIST
}
}
#else // USE_CLUSTERED_LIGHTLIST
// Advance to the next volume in one (or both at the same time) clusters.
if (volumeIndex == volumeIndices[0])
{
i++;
volumeIndices[0] = FetchIndexWithBoundsCheck(volumeStarts[0], volumeCounts[0], i);
}
if (volumeIndex == volumeIndices[1])
{
j++;
volumeIndices[1] = FetchIndexWithBoundsCheck(volumeStarts[1], volumeCounts[1], j);
}
} while (i < volumeCounts[0] || j < volumeCounts[1]);
}
// We don't need to carry over the cluster index, only the start and the count.
volumeStarts[0] = volumeStarts[1];
volumeCounts[0] = volumeCounts[1];
#endif // USE_CLUSTERED_LIGHTLIST
_VBufferDensity[voxelCoord] = float4(voxelScattering, voxelExtinction);
z0 = z1;
}
}
[numthreads(GROUP_SIZE_1D, GROUP_SIZE_1D, 1)]
void VolumeVoxelization(uint2 groupId : SV_GroupID,
uint2 groupThreadId : SV_GroupThreadID)
{
// Perform compile-time checks.
if (!IsPower2(VBUFFER_TILE_SIZE) || !IsPower2(TILE_SIZE_CLUSTERED)) return;
uint2 groupCoord = groupThreadId;
uint2 groupOffset = groupId * GROUP_SIZE_1D;
uint2 voxelCoord = groupOffset + groupCoord;
uint2 tileCoord = voxelCoord * VBUFFER_TILE_SIZE / TILE_SIZE_CLUSTERED;
uint voxelsPerClusterTile = Sq((uint)(TILE_SIZE_CLUSTERED / VBUFFER_TILE_SIZE));
if (voxelsPerClusterTile >= 64)
{
// TODO: this is a compile-time test, make sure the compiler actually scalarizes.
tileCoord = groupOffset * VBUFFER_TILE_SIZE / TILE_SIZE_CLUSTERED;
}
UNITY_BRANCH
if (voxelCoord.x >= (uint)_VBufferResolution.x ||
voxelCoord.y >= (uint)_VBufferResolution.y)
{
return;
}
// Reminder: our voxel is a skewed pyramid frustum with square front and back faces.
// Compute 3x orthogonal directions.
float2 centerCoord = voxelCoord + float2( 0.5, 0.5);
float2 leftCoord = voxelCoord + float2(-0.5, 0.5);
float2 upCoord = voxelCoord + float2( 0.5, -0.5);
// TODO: avoid 2x matrix multiplications by precomputing the world-space offset on the vs_Z=1 plane.
// Compute 3x ray directions s.t. its ViewSpace(rayDirWS).z = 1.
float3 centerDirWS = mul(-float3(centerCoord, 1), (float3x3)_VBufferCoordToViewDirWS);
float3 leftDirWS = mul(-float3(leftCoord, 1), (float3x3)_VBufferCoordToViewDirWS);
float3 upDirWS = mul(-float3(upCoord, 1), (float3x3)_VBufferCoordToViewDirWS);
// Compute the axes of the voxel. These are not normalized, but rather computed to scale with Z.
float3 voxelAxisForward = centerDirWS;
float3 voxelAxisUp = 0.5 * (upDirWS - centerDirWS);
float3 voxelAxisRight = 0.5 * (centerDirWS - leftDirWS);
PositionInputs posInput = GetPositionInput(voxelCoord, _VBufferResolution.zw, tileCoord);
FillVolumetricDensityBuffer(posInput, GetCurrentViewPosition(), centerDirWS,
voxelAxisRight, voxelAxisUp, voxelAxisForward);
}

8
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumeVoxelization.compute.meta


fileFormatVersion: 2
guid: c20b371db720da244b73830ec74a343a
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 4
userData:
assetBundleName:
assetBundleVariant:

8
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute.meta


fileFormatVersion: 2
guid: b4901a10df2d1e24282725e9fbc77c97
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 4
userData:
assetBundleName:
assetBundleVariant:

10
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/Resources.meta


fileFormatVersion: 2
guid: 333b470add5766f44a744f476efc19a8
folderAsset: yes
timeCreated: 1503591964
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

/ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/Resources/VolumetricLighting.compute → /ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute

正在加载...
取消
保存