浏览代码

Fix issues with light leaking in volumetric lighting

(due to sub-allocation (resource aliasing) of the RTHandle system)
/main
Evgenii Golubev 7 年前
当前提交
e7f0a6d1
共有 11 个文件被更改,包括 98 次插入57 次删除
  1. 9
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Camera/HDCamera.cs
  2. 4
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/Camera/HDCameraEditor.Handlers.cs
  3. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/Camera/HDCameraEditor.cs
  4. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDRenderPipeline.cs
  5. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDStringConstants.cs
  6. 6
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Reflection/ReflectionSystemInternal.cs
  7. 35
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VBuffer.hlsl
  8. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.compute
  9. 87
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs
  10. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderVariables.hlsl
  11. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Sky/AtmosphericScattering/AtmosphericScattering.hlsl

9
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Camera/HDCamera.cs


Reset();
}
public void Update(PostProcessLayer postProcessLayer, FrameSettings frameSettings)
// Pass all the systems that may want to update per-camera data here.
// That way you will never update an HDCamera and forget to update the dependent system.
public void Update(PostProcessLayer postProcessLayer, FrameSettings frameSettings, VolumetricLightingSystem vlSys)
{
// If TAA is enabled projMatrix will hold a jittered projection matrix. The original,
// non-jittered projection matrix can be accessed via nonJitteredProjMatrix.

screenSize = new Vector4(screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
screenParams = new Vector4(screenSize.x, screenSize.y, 1 + screenSize.z, 1 + screenSize.w);
if (vlSys != null)
{
vlSys.UpdatePerCameraData(this);
}
}
// Stopgap method used to extract stereo combined matrix state.

4
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/Camera/HDCameraEditor.Handlers.cs


using System;
using System;
using System.Reflection;
using UnityEngine;
using UnityEngine.Experimental.Rendering;

// And then to copy the runtime frame settings
// So this includes the runtime frame settings properly
cameraData.GetFrameSettings().CopyTo(m_PreviewAdditionalCameraData.GetFrameSettings());
m_PreviewHDCamera.Update(m_PreviewPostProcessLayer, m_PreviewAdditionalCameraData.GetFrameSettings());
m_PreviewHDCamera.Update(m_PreviewPostProcessLayer, m_PreviewAdditionalCameraData.GetFrameSettings(), null);
var previewTexture = GetPreviewTextureWithSize((int)previewSize.x, (int)previewSize.y);
m_PreviewCamera.targetTexture = previewTexture;

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/Camera/HDCameraEditor.cs


m_PreviewAdditionalCameraData = m_PreviewCamera.gameObject.AddComponent<HDAdditionalCameraData>();
m_PreviewPostProcessLayer = m_PreviewCamera.gameObject.AddComponent<PostProcessLayer>();
m_PreviewHDCamera = new HDCamera(m_PreviewCamera);
m_PreviewHDCamera.Update(m_PreviewPostProcessLayer, m_PreviewAdditionalCameraData.GetFrameSettings());
m_PreviewHDCamera.Update(m_PreviewPostProcessLayer, m_PreviewAdditionalCameraData.GetFrameSettings(), null);
}
void OnDisable()

3
ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDRenderPipeline.cs


hdCamera = HDCamera.Create(camera, m_VolumetricLightingSystem);
}
hdCamera.Update(postProcessLayer, m_FrameSettings);
m_VolumetricLightingSystem.UpdatePerCameraData(hdCamera);
hdCamera.Update(postProcessLayer, m_FrameSettings, m_VolumetricLightingSystem);
Resize(hdCamera);

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDStringConstants.cs


public static readonly int _CornetteShanksConstant = Shader.PropertyToID("_CornetteShanksConstant");
public static readonly int _VBufferResolution = Shader.PropertyToID("_VBufferResolution");
public static readonly int _VBufferSliceCount = Shader.PropertyToID("_VBufferSliceCount");
public static readonly int _VBufferUvScaleAndLimit = Shader.PropertyToID("_VBufferUvScaleAndLimit");
public static readonly int _VBufferPrevUvScaleAndLimit = Shader.PropertyToID("_VBufferPrevUvScaleAndLimit");
public static readonly int _VBufferPrevDepthEncodingParams = Shader.PropertyToID("_VBufferPrevDepthEncodingParams");
public static readonly int _VBufferPrevDepthDecodingParams = Shader.PropertyToID("_VBufferPrevDepthDecodingParams");
public static readonly int _VBufferCoordToViewDirWS = Shader.PropertyToID("_VBufferCoordToViewDirWS");

6
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Reflection/ReflectionSystemInternal.cs


hdCamera = HDCamera.Create(renderCamera, null);
}
hdCamera.Update(null, probe.frameSettings);
hdCamera.Update(null, probe.frameSettings, null);
if (!IsRealtimeTextureValid(probe.realtimeTexture, hdCamera))
{

hdCamera = HDCamera.Create(camera, null);
}
hdCamera.Update(null, probe.frameSettings);
hdCamera.Update(null, probe.frameSettings, null);
if (!IsRealtimeTextureValid(probe.realtimeTexture, hdCamera))
{

hdCamera = HDCamera.Create(camera, null);
}
hdCamera.Update(null, probe.frameSettings);
hdCamera.Update(null, probe.frameSettings, null);
return hdCamera;
}

35
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VBuffer.hlsl


float4 SampleVBuffer(TEXTURE3D_ARGS(VBuffer, clampSampler),
float2 positionNDC,
float linearDepth,
float2 viewportScale,
float2 VBufferUvScale,
float2 VBufferUvLimit,
float4 VBufferDepthEncodingParams,
float4 VBufferDepthDecodingParams,
bool correctLinearInterpolation,

w = d;
}
// Always clamp UVs (clamp to edge) to avoid leaks due to suballocation and memory aliasing.
// clamp to (vp_dim - 0.5) / tex_dim = vp_scale - (0.5 / tex_dim) = vp_scale - 0.5 * vp_scale / tex_dim.
// Do not clamp along the W direction for now, it's not necessary (our slice count is fixed).
// Warning: there will still be some leaks as long as the viewport, screen or texture size
// are not multiples of the V-Buffer tile size (8 or 4 pixels). We ignore them for now since
// it's not a problem in for a real game.
// TODO: precompute this in a uniform...
float2 maxUV = viewportScale * (1 - 0.5 * VBufferResolution.zw);
float fadeWeight = 1;
if (clampToBorder)

BiquadraticFilter(1 - fc, weights, offsets); // Inverse-translate the filter centered around 0.5
// Apply the viewport scale right at the end.
// TODO: precompute (VBufferResolution.zw * viewportScale).
result = (weights[0].x * weights[0].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[0].x, offsets[0].y)) * (VBufferResolution.zw * viewportScale), maxUV), w), 0) // Top left
+ (weights[1].x * weights[0].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[1].x, offsets[0].y)) * (VBufferResolution.zw * viewportScale), maxUV), w), 0) // Top right
+ (weights[0].x * weights[1].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[0].x, offsets[1].y)) * (VBufferResolution.zw * viewportScale), maxUV), w), 0) // Bottom left
+ (weights[1].x * weights[1].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[1].x, offsets[1].y)) * (VBufferResolution.zw * viewportScale), maxUV), w), 0); // Bottom right
// TODO: precompute (VBufferResolution.zw * VBufferUvScale).
result = (weights[0].x * weights[0].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[0].x, offsets[0].y)) * (VBufferResolution.zw * VBufferUvScale), VBufferUvLimit), w), 0) // Top left
+ (weights[1].x * weights[0].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[1].x, offsets[0].y)) * (VBufferResolution.zw * VBufferUvScale), VBufferUvLimit), w), 0) // Top right
+ (weights[0].x * weights[1].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[0].x, offsets[1].y)) * (VBufferResolution.zw * VBufferUvScale), VBufferUvLimit), w), 0) // Bottom left
+ (weights[1].x * weights[1].y) * SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min((ic + float2(offsets[1].x, offsets[1].y)) * (VBufferResolution.zw * VBufferUvScale), VBufferUvLimit), w), 0); // Bottom right
result = SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min(uv * viewportScale, maxUV), w), 0);
result = SAMPLE_TEXTURE3D_LOD(VBuffer, clampSampler, float3(min(uv * VBufferUvScale, VBufferUvLimit), w), 0);
}
result *= fadeWeight;

float4 SampleVBuffer(TEXTURE3D_ARGS(VBuffer, clampSampler),
float3 positionWS,
float4x4 viewProjMatrix,
float2 viewportScale,
float2 VBufferUvScale,
float2 VBufferUvLimit,
float4 VBufferDepthEncodingParams,
float4 VBufferDepthDecodingParams,
bool correctLinearInterpolation,

return SampleVBuffer(TEXTURE3D_PARAM(VBuffer, clampSampler),
positionNDC,
linearDepth,
viewportScale,
VBufferUvScale,
VBufferUvLimit,
VBufferDepthEncodingParams,
VBufferDepthDecodingParams,
correctLinearInterpolation,

float linearDepth,
float4 VBufferResolution,
float2 VBufferSliceCount,
float2 VBufferUvScale,
float2 VBufferUvLimit,
float4 VBufferDepthEncodingParams,
float4 VBufferDepthDecodingParams,
bool correctLinearInterpolation,

return FastTonemapInvert(SampleVBuffer(TEXTURE3D_PARAM(VBufferLighting, clampSampler),
positionNDC,
linearDepth,
GetViewportScaleCurrentFrame(),
VBufferUvScale,
VBufferUvLimit,
VBufferDepthEncodingParams,
VBufferDepthDecodingParams,
correctLinearInterpolation,

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


float4 reprojValue = SampleVBuffer(TEXTURE3D_PARAM(_VBufferLightingHistory, s_linear_clamp_sampler),
centerWS,
_PrevViewProjMatrix,
1, // GetViewportScalePreviousFrame(),
_VBufferPrevUvScaleAndLimit.xy,
_VBufferPrevUvScaleAndLimit.zw,
_VBufferPrevDepthEncodingParams,
_VBufferPrevDepthDecodingParams,
false, false, true);

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


{
public Vector4 resolution;
public Vector2 sliceCount;
public Vector4 uvScaleAndLimit; // Necessary us to work with sub-allocation (resource aliasing) in the RTHandle system
public VBufferParameters(int w, int h, int d, ControllerParameters controlParams)
public VBufferParameters(Vector3Int viewportResolution, Vector3Int bufferResolution, ControllerParameters controlParams)
int w = viewportResolution.x;
int h = viewportResolution.y;
int d = viewportResolution.z;
// The depth is fixed for now.
Vector2 uvScale = new Vector2((float)w / (float)bufferResolution.x,
(float)h / (float)bufferResolution.y);
// vp_scale = vp_dim / tex_dim.
// clamp to (vp_dim - 0.5) / tex_dim = vp_scale - 0.5 * (1 / tex_dim) =
// vp_scale - 0.5 * (vp_scale / vp_dim) = vp_scale * (1 - 0.5 / vp_dim).
Vector2 uvLimit = new Vector2((w - 0.5f) / (float)bufferResolution.x,
(h - 0.5f) / (float)bufferResolution.y);
uvScaleAndLimit = new Vector4(uvScale.x, uvScale.y, uvLimit.x, uvLimit.y);
depthEncodingParams = Vector4.zero; // C# doesn't allow function calls before all members have been init
depthDecodingParams = Vector4.zero; // C# doesn't allow function calls before all members have been init

}
// RTHandleSystem API expects a function which computes the resolution. We define it here.
Vector2Int ComputeVBufferSizeXY(Vector2Int screenSize)
Vector2Int ComputeVBuffeResolutionXY(Vector2Int screenSize)
int t = ComputeVBufferTileSize(preset);
Vector3Int resolution = ComputeVBufferResolution(preset, screenSize.x, screenSize.y);
// Ceil(ScreenSize / TileSize).
int w = (screenSize.x + (t - 1)) / t;
int h = (screenSize.y + (t - 1)) / t;
// Since the buffers owned by the VolumetricLightingSystem may have different lifetimes compared
// to those owned by the HDCamera, we need to make sure that the buffer resolution is the same
// (in order to share the UV scale and the UV limit).
if (m_LightingBufferHandle != null)
{
resolution.x = Math.Max(resolution.x, m_LightingBufferHandle.rt.width);
resolution.y = Math.Max(resolution.y, m_LightingBufferHandle.rt.height);
}
return new Vector2Int(w, h);
return new Vector2Int(resolution.x, resolution.y);
}
// BufferedRTHandleSystem API expects an allocator function. We define it here.

int d = ComputeVBufferSliceCount(preset);
return rtHandleSystem.Alloc(scaleFunc: ComputeVBufferSizeXY,
return rtHandleSystem.Alloc(scaleFunc: ComputeVBuffeResolutionXY,
slices: d,
dimension: TextureDimension.Tex3D,
colorFormat: RenderTextureFormat.ARGBHalf,

int d = ComputeVBufferSliceCount(preset);
m_DensityBufferHandle = RTHandles.Alloc(scaleFunc: ComputeVBufferSizeXY,
m_DensityBufferHandle = RTHandles.Alloc(scaleFunc: ComputeVBuffeResolutionXY,
slices: d,
dimension: TextureDimension.Tex3D,
colorFormat: RenderTextureFormat.ARGBHalf,

/* useDynamicScale: true, // <- TODO */
name: "VBufferDensity");
m_LightingBufferHandle = RTHandles.Alloc(scaleFunc: ComputeVBufferSizeXY,
m_LightingBufferHandle = RTHandles.Alloc(scaleFunc: ComputeVBuffeResolutionXY,
slices: d,
dimension: TextureDimension.Tex3D,
colorFormat: RenderTextureFormat.ARGBHalf,

name: "VBufferIntegral");
}
VBufferParameters ComputeVBufferParameters(HDCamera camera)
// For the initial allocation, no suballocation happens (the texture is full size).
VBufferParameters ComputeVBufferParameters(HDCamera camera, bool isInitialAllocation)
{
ControllerParameters controlParams;

controlParams = ControllerParameters.GetDefaults();
}
int w = 0, h = 0, d = 0;
ComputeVBufferResolutionAndScale(preset, camera.camera.pixelWidth, camera.camera.pixelHeight, ref w, ref h, ref d);
Vector3Int viewportResolution = ComputeVBufferResolution(preset, camera.camera.pixelWidth, camera.camera.pixelHeight);
Vector3Int bufferResolution; // Could be higher due to sub-allocation (resource aliasing) in the RTHandle system
if (isInitialAllocation)
{
bufferResolution = viewportResolution;
}
else
{
// All V-Buffers of the current frame should have the same size (you have to double-buffer history, of course).
bufferResolution = new Vector3Int(m_LightingBufferHandle.rt.width, m_LightingBufferHandle.rt.height, m_LightingBufferHandle.rt.volumeDepth);
}
return new VBufferParameters(w, h, d, controlParams);
return new VBufferParameters(viewportResolution, bufferResolution, controlParams);
}
public void InitializePerCameraData(HDCamera camera)

// Start with the same parameters for both frames. Then update them one by one every frame.
var parameters = ComputeVBufferParameters(camera);
var parameters = ComputeVBufferParameters(camera, true);
camera.vBufferParams = new VBufferParameters[2];
camera.vBufferParams[0] = parameters;
camera.vBufferParams[1] = parameters;

{
if (preset == VolumetricLightingPreset.Off) return;
var parameters = ComputeVBufferParameters(camera);
var parameters = ComputeVBufferParameters(camera, false);
// Double-buffer. I assume the cost of copying is negligible (don't want to use the frame index).
camera.vBufferParams[1] = camera.vBufferParams[0];

}
}
// Since a single voxel corresponds to a tile (e.g. 8x8) of pixels,
// the VBuffer can potentially extend past the boundaries of the viewport.
// The function returns the fraction of the {width, height} of the VBuffer visible on screen.
// Note: for performance reasons, the scale is unused (implicitly 1). The error is typically under 1%.
static Vector2 ComputeVBufferResolutionAndScale(VolumetricLightingPreset preset,
int screenWidth, int screenHeight,
ref int w, ref int h, ref int d)
static Vector3Int ComputeVBufferResolution(VolumetricLightingPreset preset,
int screenWidth, int screenHeight)
// Ceil(ScreenSize / TileSize).
w = (screenWidth + (t - 1)) / t;
h = (screenHeight + (t - 1)) / t;
d = ComputeVBufferSliceCount(preset);
// ceil(ScreenSize / TileSize).
int w = (screenWidth + (t - 1)) / t;
int h = (screenHeight + (t - 1)) / t;
int d = ComputeVBufferSliceCount(preset);
return new Vector2((float)screenWidth / (float)(w * t), (float)screenHeight / (float)(h * t));
return new Vector3Int(w, h, d);
}
// See EncodeLogarithmicDepthGeneralized().

cmd.SetGlobalVector( HDShaderIDs._VBufferResolution, currFrameParams.resolution);
cmd.SetGlobalVector( HDShaderIDs._VBufferSliceCount, currFrameParams.sliceCount);
cmd.SetGlobalVector( HDShaderIDs._VBufferUvScaleAndLimit, currFrameParams.uvScaleAndLimit);
cmd.SetGlobalVector( HDShaderIDs._VBufferPrevUvScaleAndLimit, prevFrameParams.uvScaleAndLimit);
cmd.SetGlobalTexture(HDShaderIDs._VBufferLighting, m_LightingBufferHandle);
}

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderVariables.hlsl


float _GlobalExtinction;
float4 _VBufferResolution; // { w, h, 1/w, 1/h }
float4 _VBufferSliceCount; // { count, 1/count, 0, 0 }
float4 _VBufferUvScaleAndLimit; // Necessary us to work with sub-allocation (resource aliasing) in the RTHandle system
float4 _VBufferDepthEncodingParams; // See the call site for description
float4 _VBufferDepthDecodingParams; // See the call site for description

float4 _VBufferPrevResolution;
float4 _VBufferPrevSliceCount;
float4 _VBufferPrevUvScaleAndLimit;
float4 _VBufferPrevDepthEncodingParams;
float4 _VBufferPrevDepthDecodingParams;
CBUFFER_END

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Sky/AtmosphericScattering/AtmosphericScattering.hlsl


posInput.linearDepth,
_VBufferResolution,
_VBufferSliceCount.xy,
_VBufferUvScaleAndLimit.xy,
_VBufferUvScaleAndLimit.zw,
_VBufferDepthEncodingParams,
_VBufferDepthDecodingParams,
true, true);

正在加载...
取消
保存