update AutoExposure
This commit is contained in:
parent
d694bdc43b
commit
2d834cf06a
@ -4707,6 +4707,10 @@ PrefabInstance:
|
||||
propertyPath: m_Name
|
||||
value: NestedParentArmature_Unpack
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8508310942573128499, guid: c708a3b79cd542b42bbfedb17e213bc1, type: 3}
|
||||
propertyPath: m_RenderPostProcessing
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents:
|
||||
- {fileID: 3543048675319490572, guid: c708a3b79cd542b42bbfedb17e213bc1, type: 3}
|
||||
m_RemovedGameObjects: []
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
|
||||
@ -63,6 +63,41 @@ MonoBehaviour:
|
||||
- {fileID: 5935297997615179469}
|
||||
- {fileID: -4720686990112659349}
|
||||
- {fileID: 9109530758031880643}
|
||||
- {fileID: 4702212335856883116}
|
||||
--- !u!114 &4702212335856883116
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 3
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: af08f034e95c2d246b0cbaf4305dfbc4, type: 3}
|
||||
m_Name: AutoExposureVolumeProfile
|
||||
m_EditorClassIdentifier:
|
||||
active: 1
|
||||
filtering:
|
||||
m_OverrideState: 0
|
||||
m_Value: {x: 50, y: 95}
|
||||
minLuminance:
|
||||
m_OverrideState: 0
|
||||
m_Value: 0
|
||||
maxLuminance:
|
||||
m_OverrideState: 0
|
||||
m_Value: 0
|
||||
exposureCompensation:
|
||||
m_OverrideState: 0
|
||||
m_Value: 1
|
||||
eyeAdaptation:
|
||||
m_OverrideState: 0
|
||||
m_Value: 0
|
||||
speedUp:
|
||||
m_OverrideState: 0
|
||||
m_Value: 2
|
||||
speedDown:
|
||||
m_OverrideState: 0
|
||||
m_Value: 1
|
||||
--- !u!114 &5935297997615179469
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 3
|
||||
|
||||
@ -75,7 +75,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 5a00a63fdd6bd2a45ab1f2d869305ffd, type: 3}
|
||||
m_Name: OasisFogVolumeComponent
|
||||
m_EditorClassIdentifier:
|
||||
active: 1
|
||||
active: 0
|
||||
Density:
|
||||
m_OverrideState: 1
|
||||
m_Value: 0.002
|
||||
@ -103,7 +103,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 66f335fb1ffd8684294ad653bf1c7564, type: 3}
|
||||
m_Name: ColorAdjustments
|
||||
m_EditorClassIdentifier:
|
||||
active: 1
|
||||
active: 0
|
||||
postExposure:
|
||||
m_OverrideState: 1
|
||||
m_Value: 2
|
||||
|
||||
@ -108,6 +108,25 @@ MonoBehaviour:
|
||||
cutoffThreshold: 0.2
|
||||
binaryValue: 0.9
|
||||
flags: 13
|
||||
--- !u!114 &-7143664486661302651
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d34dacd53c1ace84a8c6ac5ff4afb555, type: 3}
|
||||
m_Name: AutoExposure
|
||||
m_EditorClassIdentifier:
|
||||
m_Active: 1
|
||||
settings:
|
||||
HistogramRenderPassEvent: 550
|
||||
AutoExposureRenderPassEvent: 550
|
||||
BlitMat: {fileID: 2100000, guid: b04591ed716b35e41857f554d491ef4b, type: 2}
|
||||
ComputeHistogramComputeShader: {fileID: 7200000, guid: b140e27dc74a1fb4d9ae30f8566b8919, type: 3}
|
||||
AutoExposureComputeShader: {fileID: 7200000, guid: 6febb5a945f3510429c58ed4a45c1846, type: 3}
|
||||
--- !u!114 &-5418649131825517062
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -337,7 +356,8 @@ MonoBehaviour:
|
||||
- {fileID: 7541218312462517771}
|
||||
- {fileID: 5808157236138506604}
|
||||
- {fileID: -5418649131825517062}
|
||||
m_RendererFeatureMap: bc3f630842f2e70dd6a559c442a94bfd4529d15534f2d3de228858dca8d12222716523fbf3439fdb7a327b7bff4bdd446ac59dfa966ffa88ca6373cd5da9013d6cff55ca297e5e908a7b3653203b82383b2141bb05fbe69aec5704e48e2763e90bc6ff9f19caa7686c79a6bb3bb89a50faad0fe75217cdb4
|
||||
- {fileID: -7143664486661302651}
|
||||
m_RendererFeatureMap: bc3f630842f2e70dd6a559c442a94bfd4529d15534f2d3de228858dca8d12222716523fbf3439fdb7a327b7bff4bdd446ac59dfa966ffa88ca6373cd5da9013d6cff55ca297e5e908a7b3653203b82383b2141bb05fbe69aec5704e48e2763e90bc6ff9f19caa7686c79a6bb3bb89a50faad0fe75217cdb485d6fa85ff9adc9c
|
||||
m_UseNativeRenderPass: 0
|
||||
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
||||
shaders:
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Drawing.Drawing2D;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
@ -10,57 +12,131 @@ namespace X.Rendering.Feature
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
|
||||
public RenderPassEvent HistogramRenderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
|
||||
public RenderPassEvent AutoExposureRenderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
|
||||
public Material BlitMat;
|
||||
public ComputeShader Compute;
|
||||
public ComputeShader ComputeHistogramComputeShader;
|
||||
public ComputeShader AutoExposureComputeShader;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
Settings settings;
|
||||
AutoExposurePass autoExposurePass;
|
||||
ComputeLuminanceHistogramPass computeLuminanceHistogramPass;
|
||||
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if(renderingData.cameraData.isSceneViewCamera || renderingData.cameraData.isPreviewCamera)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
renderer.EnqueuePass(computeLuminanceHistogramPass);
|
||||
renderer.EnqueuePass(autoExposurePass);
|
||||
}
|
||||
|
||||
public override void Create()
|
||||
{
|
||||
computeLuminanceHistogramPass = new(settings);
|
||||
autoExposurePass = new(settings);
|
||||
}
|
||||
|
||||
class AutoExposurePass : ScriptableRenderPass
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
autoExposurePass?.Dispose();
|
||||
computeLuminanceHistogramPass?.Dispose();
|
||||
}
|
||||
|
||||
class ComputeLuminanceHistogramPass : ScriptableRenderPass, IDisposable
|
||||
{
|
||||
private ProfilingSampler profiler;
|
||||
private Settings settings;
|
||||
private GraphicsBuffer histogramBuffer;
|
||||
|
||||
public const int k_Bins = 128;
|
||||
public static GraphicsBufferHandle HistogramBufferHandle;
|
||||
|
||||
public ComputeLuminanceHistogramPass(Settings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
profiler = new(nameof(ComputeLuminanceHistogramPass));
|
||||
renderPassEvent = settings.HistogramRenderPassEvent;
|
||||
histogramBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, k_Bins, sizeof(uint));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
histogramBuffer?.Release();
|
||||
histogramBuffer = null;
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
var stack = VolumeManager.instance.stack;
|
||||
var autoExposureVolume = stack.GetComponent<AutoExposureVolumeProfile>();
|
||||
if (!autoExposureVolume || !autoExposureVolume.IsActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var cmd = renderingData.commandBuffer;
|
||||
using var scp = new ProfilingScope(cmd, profiler);
|
||||
var scaleOffsetRes = AutoExposureVolumeProfile.GetHistogramScaleOffsetRes();
|
||||
var computeShader = settings.ComputeHistogramComputeShader;
|
||||
int kernel = computeShader.FindKernel("KEyeHistogramClear");
|
||||
cmd.SetComputeBufferParam(computeShader, kernel, "_HistogramBuffer", histogramBuffer);
|
||||
computeShader.GetKernelThreadGroupSizes(kernel, out var threadX, out var threadY, out var threadZ);
|
||||
cmd.DispatchCompute(computeShader, kernel, Mathf.CeilToInt(k_Bins / (float)threadX), 1, 1);
|
||||
|
||||
// Get a log histogram
|
||||
kernel = 1;
|
||||
HistogramBufferHandle = histogramBuffer.bufferHandle;
|
||||
cmd.SetComputeBufferParam(computeShader, kernel, "_HistogramBuffer", histogramBuffer);
|
||||
cmd.SetComputeTextureParam(computeShader, kernel, "_Source", renderingData.cameraData.renderer.cameraColorTargetHandle);
|
||||
cmd.SetComputeVectorParam(computeShader, "_ScaleOffsetRes", scaleOffsetRes);
|
||||
|
||||
computeShader.GetKernelThreadGroupSizes(kernel, out threadX, out threadY, out threadZ);
|
||||
|
||||
cmd.DispatchCompute(computeShader, kernel,
|
||||
Mathf.CeilToInt(scaleOffsetRes.z / 2f / threadX),
|
||||
Mathf.CeilToInt(scaleOffsetRes.w / 2f / threadY),
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
class AutoExposurePass : ScriptableRenderPass, IDisposable
|
||||
{
|
||||
private Settings settings;
|
||||
private int computeCounter;
|
||||
private ProfilingSampler profiler;
|
||||
private ComputeBuffer outputBuffer;
|
||||
|
||||
private static readonly int Dims = Shader.PropertyToID("_Dims");
|
||||
private static readonly int Source = Shader.PropertyToID("_Source");
|
||||
private static readonly int TempOut = Shader.PropertyToID("_OutBuffer");
|
||||
private static readonly int ScreenExposureProp = Shader.PropertyToID("_Exposure");
|
||||
private static readonly int ExposureRangeProp = Shader.PropertyToID("_Range");
|
||||
private static readonly int WhitePointProp = Shader.PropertyToID("_WhitePoint");
|
||||
private static readonly int MaxBrightness = Shader.PropertyToID("_MaxBrightness");
|
||||
private static readonly int BlitTextureId = Shader.PropertyToID("_BlitTexture");
|
||||
|
||||
const int k_NumAutoExposureTextures = 2;
|
||||
private RTHandle[] autoExposureHandles;
|
||||
private int autoExposurePingPong;
|
||||
|
||||
private const int ThreadGroupSize = 32;
|
||||
|
||||
private Vector2Int lastSize = Vector2Int.zero;
|
||||
|
||||
private static float targetExposure = 0.1f;
|
||||
private static float currentExposure = 0.1f;
|
||||
private static AutoExposureVolumeProfile autoExposureVolume = null;
|
||||
private static float lastTime = 0.0f;
|
||||
private static bool autoExposureing = false;
|
||||
private bool reset = false;
|
||||
|
||||
public AutoExposurePass(Settings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
renderPassEvent = settings.renderPassEvent;
|
||||
renderPassEvent = settings.AutoExposureRenderPassEvent;
|
||||
profiler = new(nameof(AutoExposurePass));
|
||||
|
||||
var desc = new RenderTextureDescriptor(1,1,GraphicsFormat.R32_SFloat,0,1)
|
||||
{
|
||||
enableRandomWrite = true,
|
||||
msaaSamples = 1,
|
||||
useMipMap = false
|
||||
};
|
||||
|
||||
autoExposureHandles = new RTHandle[k_NumAutoExposureTextures];
|
||||
|
||||
for (var i = 0; i < autoExposureHandles.Length; i++)
|
||||
{
|
||||
RenderingUtils.ReAllocateIfNeeded(ref autoExposureHandles[i], desc, name: $"AutoExposure_{i}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
@ -69,97 +145,89 @@ namespace X.Rendering.Feature
|
||||
autoExposureVolume = stack.GetComponent<AutoExposureVolumeProfile>();
|
||||
if (!autoExposureVolume || !autoExposureVolume.IsActive())
|
||||
{
|
||||
reset = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var cmd = renderingData.commandBuffer;
|
||||
using var scp = new ProfilingScope(cmd, profiler);
|
||||
if (computeCounter % Mathf.Max(autoExposureVolume.framesPerCompute.value, 1.0f) == 0)
|
||||
var filteringMinMax = autoExposureVolume.filtering.value;
|
||||
float lowPercent = filteringMinMax.x;
|
||||
float highPercent = filteringMinMax.y;
|
||||
const float kMinDelta = 1e-2f;
|
||||
highPercent = Mathf.Clamp(highPercent, 1f + kMinDelta, 99f);
|
||||
lowPercent = Mathf.Clamp(lowPercent, 1f, highPercent - kMinDelta);
|
||||
|
||||
// Clamp min/max adaptation values as well
|
||||
float minLum = autoExposureVolume.minLuminance.value;
|
||||
float maxLum = autoExposureVolume.maxLuminance.value;
|
||||
minLum = Mathf.Min(minLum, maxLum);
|
||||
maxLum = Mathf.Max(minLum, maxLum);
|
||||
var speedDown = Mathf.Max(0, autoExposureVolume.speedDown.value);
|
||||
var speedUp = Mathf.Max(0, autoExposureVolume.speedUp.value);
|
||||
var exposureCompensation = autoExposureVolume.exposureCompensation.value;
|
||||
string adaptation = null;
|
||||
|
||||
if (autoExposureVolume.eyeAdaptation.value == AutoExposureVolumeProfile.EyeAdaptation.Fixed)
|
||||
adaptation = "KAutoExposureAvgLuminance_fixed";
|
||||
else
|
||||
adaptation = "KAutoExposureAvgLuminance_progressive";
|
||||
|
||||
var compute = settings.AutoExposureComputeShader;
|
||||
int kernel = compute.FindKernel(adaptation);
|
||||
int pp = autoExposurePingPong;
|
||||
var src = autoExposureHandles[++pp % 2];
|
||||
var dst = autoExposureHandles[++pp % 2];
|
||||
|
||||
if (reset)
|
||||
{
|
||||
computeCounter = 0;
|
||||
var textureDesc = renderingData.cameraData.cameraTargetDescriptor;
|
||||
|
||||
Vector2Int size = new(textureDesc.width, textureDesc.height);
|
||||
if(outputBuffer == null || lastSize != size)
|
||||
{
|
||||
lastSize = size;
|
||||
outputBuffer?.Dispose();
|
||||
outputBuffer = new ComputeBuffer(Mathf.CeilToInt((float)size.y / ThreadGroupSize * 2), 4, ComputeBufferType.Structured)
|
||||
{
|
||||
name = "Auto Exposure Output Buffer"
|
||||
};
|
||||
}
|
||||
cmd.SetComputeIntParams(settings.Compute, Dims, size.x, size.y);
|
||||
|
||||
cmd.SetComputeTextureParam(settings.Compute, 0, Source, renderingData.cameraData.renderer.cameraColorTargetHandle);
|
||||
cmd.SetComputeBufferParam(settings.Compute, 0, TempOut, outputBuffer);
|
||||
cmd.DispatchCompute(settings.Compute, 0, size.y, 1, 1);
|
||||
AsyncGPUReadback.Request(outputBuffer, OnCompleteReadBack);
|
||||
cmd.SetRenderTarget(src, loadAction: RenderBufferLoadAction.DontCare, storeAction: RenderBufferStoreAction.Store);
|
||||
cmd.ClearRenderTarget(false, true, Color.clear);
|
||||
}
|
||||
++computeCounter;
|
||||
if (!autoExposureing)
|
||||
{
|
||||
//return;
|
||||
}
|
||||
UpdateExposure();
|
||||
settings.BlitMat.SetFloat(ScreenExposureProp, currentExposure);
|
||||
settings.BlitMat.SetVector(ExposureRangeProp, autoExposureVolume.exposureRange.value);
|
||||
settings.BlitMat.SetFloat(WhitePointProp, autoExposureVolume.whitePoint.value);
|
||||
settings.BlitMat.SetFloat(MaxBrightness, autoExposureVolume.brightnessLimit.value);
|
||||
|
||||
cmd.SetComputeBufferParam(compute, kernel, "_HistogramBuffer", ComputeLuminanceHistogramPass.HistogramBufferHandle);
|
||||
cmd.SetComputeVectorParam(compute, "_Params1", new Vector4(lowPercent * 0.01f, highPercent * 0.01f, Mathf.Pow(2, minLum), Mathf.Pow(2, maxLum)));
|
||||
cmd.SetComputeVectorParam(compute, "_Params2", new Vector4(speedDown, speedUp, exposureCompensation, Time.smoothDeltaTime));
|
||||
cmd.SetComputeVectorParam(compute, "_ScaleOffsetRes", AutoExposureVolumeProfile.GetHistogramScaleOffsetRes());
|
||||
|
||||
cmd.SetComputeTextureParam(compute, kernel, "_Source", src);
|
||||
cmd.SetComputeTextureParam(compute, kernel, "_Destination", dst);
|
||||
cmd.DispatchCompute(compute, kernel, 1, 1, 1);
|
||||
autoExposurePingPong = ++pp % 2;
|
||||
|
||||
|
||||
var renderer = renderingData.cameraData.renderer;
|
||||
settings.BlitMat.SetTexture(BlitTextureId, renderer.cameraColorTargetHandle);
|
||||
settings.BlitMat.SetTexture("_SourceTexture", renderer.cameraColorTargetHandle);
|
||||
settings.BlitMat.SetTexture("_AutoExposureTexture", dst);
|
||||
var destination = renderer.GetCameraColorFrontBuffer(cmd);
|
||||
cmd.SetRenderTarget(destination, loadAction: RenderBufferLoadAction.DontCare, storeAction: RenderBufferStoreAction.Store);
|
||||
cmd.DrawProcedural(Matrix4x4.identity, settings.BlitMat, 0, MeshTopology.Triangles, 3);
|
||||
renderer.SwapColorBuffer(cmd);
|
||||
reset = false;
|
||||
//cmd.RequestAsyncReadback(dst, (a) =>
|
||||
//{
|
||||
// if (!a.hasError)
|
||||
// {
|
||||
// var data = a.GetData<float>();
|
||||
// for (var i = 0; i < data.Length; i++)
|
||||
// {
|
||||
// Debug.Log(data[i]);
|
||||
// }
|
||||
// }
|
||||
//});
|
||||
}
|
||||
|
||||
private static void OnCompleteReadBack(AsyncGPUReadbackRequest request)
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (request.hasError)
|
||||
if (autoExposureHandles != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var autoExposureHandle in autoExposureHandles)
|
||||
{
|
||||
autoExposureHandle?.Release();
|
||||
}
|
||||
|
||||
// Get Compute result
|
||||
float[] groupValues = request.GetData<float>().ToArray();
|
||||
|
||||
// Add up all the workgroup's results
|
||||
double pixelLumTotal = 0;
|
||||
uint pixels = 0;
|
||||
for (int i = 0; i < groupValues.Length / 2; i++)
|
||||
{
|
||||
pixelLumTotal += groupValues[i * 2];
|
||||
pixels += (uint)Mathf.CeilToInt(groupValues[i * 2 + 1]);
|
||||
}
|
||||
|
||||
// Average the results and set as the target exposure
|
||||
targetExposure = (float)(pixelLumTotal / pixels);
|
||||
autoExposureing = true;
|
||||
}
|
||||
|
||||
public static float ExpDecay(float a, float b, float decay, float deltaTime) => b + (a - b) * Mathf.Exp(-decay * deltaTime);
|
||||
|
||||
private static void UpdateExposure()
|
||||
{
|
||||
// Delta time, using the built-in one seems to flicker a lot, weird
|
||||
float deltaTime = Time.time - lastTime;
|
||||
lastTime = Time.time;
|
||||
|
||||
// Exposure difference, skip if zero
|
||||
float diff = targetExposure - currentExposure;
|
||||
if (Mathf.Approximately(diff,0))
|
||||
{
|
||||
autoExposureing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float decay = diff > 0 ? autoExposureVolume.increaseSpeed.value : autoExposureVolume.decreaseSpeed.value;
|
||||
currentExposure = ExpDecay(currentExposure, targetExposure, decay, deltaTime);
|
||||
|
||||
if (float.IsNaN(currentExposure))
|
||||
{
|
||||
currentExposure = 0.1f;
|
||||
autoExposureHandles = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,82 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace X.Rendering.Feature
|
||||
{
|
||||
|
||||
|
||||
public class AutoExposureVolumeProfile : VolumeComponent, IPostProcessComponent
|
||||
{
|
||||
[Tooltip("How many frames it takes to recalculate the average brightness. Decreasing this value can affect performance.")]
|
||||
public ClampedIntParameter framesPerCompute = new(5, 1, 20);
|
||||
public const int rangeMin = -9; // ev
|
||||
public const int rangeMax = 9; // ev
|
||||
|
||||
[Space]
|
||||
[Tooltip("Limits the maximum brightness for very bright objects to work properly with bloom and other effects")]
|
||||
public FloatParameter brightnessLimit = new(15f);
|
||||
public static Vector4 GetHistogramScaleOffsetRes()
|
||||
{
|
||||
float diff = rangeMax - rangeMin;
|
||||
float scale = 1f / diff;
|
||||
float offset = -rangeMin * scale;
|
||||
return new Vector4(scale, offset, Screen.width, Screen.height);
|
||||
}
|
||||
|
||||
[Tooltip("For reinhard tonemapping")]
|
||||
public FloatParameter whitePoint = new(3f);
|
||||
public enum EyeAdaptation
|
||||
{
|
||||
/// <summary>
|
||||
/// Progressive (smooth) eye adaptation.
|
||||
/// </summary>
|
||||
Progressive,
|
||||
|
||||
[Tooltip("How much an individual pixel's exposure can vary from the original value.")]
|
||||
public FloatRangeParameter exposureRange = new(new Vector2(-2.5f, 0.6f), -6.0f, 6.0f);
|
||||
/// <summary>
|
||||
/// Fixed (instant) eye adaptation.
|
||||
/// </summary>
|
||||
Fixed
|
||||
}
|
||||
|
||||
[Space]
|
||||
[Tooltip("How quickly the exposure increases")]
|
||||
public FloatParameter increaseSpeed = new(5f);
|
||||
[Serializable]
|
||||
public sealed class EyeAdaptationParameter : VolumeParameter<EyeAdaptation> { }
|
||||
|
||||
[Tooltip("How quickly the exposure decreases")]
|
||||
public FloatParameter decreaseSpeed = new(5f);
|
||||
[Range(1f, 99f)]
|
||||
public Vector2Parameter filtering = new Vector2Parameter(new Vector2(50f, 95f));
|
||||
|
||||
public bool IsActive() => framesPerCompute.overrideState;
|
||||
/// <summary>
|
||||
/// Minimum average luminance to consider for auto exposure (in EV).
|
||||
/// </summary>
|
||||
[Range(rangeMin, rangeMax), InspectorName("Minimum (EV)")]
|
||||
public FloatParameter minLuminance = new FloatParameter (0f);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum average luminance to consider for auto exposure (in EV).
|
||||
/// </summary>
|
||||
[Range(rangeMin, rangeMax), InspectorName("Maximum (EV)")]
|
||||
public FloatParameter maxLuminance = new FloatParameter (0f);
|
||||
|
||||
/// <summary>
|
||||
/// Middle-grey value. Use this to compensate the global exposure of the scene.
|
||||
/// </summary>
|
||||
[Min(0f), InspectorName("Exposure Compensation"), Tooltip("Use this to scale the global exposure of the scene.")]
|
||||
public FloatParameter exposureCompensation = new FloatParameter (1f);
|
||||
|
||||
/// <summary>
|
||||
/// The type of eye adaptation to use.
|
||||
/// </summary>
|
||||
[InspectorName("Type"), Tooltip("Use \"Progressive\" if you want auto exposure to be animated. Use \"Fixed\" otherwise.")]
|
||||
public EyeAdaptationParameter eyeAdaptation = new EyeAdaptationParameter() { value = EyeAdaptation.Progressive };
|
||||
|
||||
/// <summary>
|
||||
/// The adaptation speed from a dark to a light environment.
|
||||
/// </summary>
|
||||
[Min(0f), Tooltip("Adaptation speed from a dark to a light environment.")]
|
||||
public FloatParameter speedUp = new FloatParameter (2f);
|
||||
|
||||
/// <summary>
|
||||
/// The adaptation speed from a light to a dark environment.
|
||||
/// </summary>
|
||||
[Min(0f), Tooltip("Adaptation speed from a light to a dark environment.")]
|
||||
public FloatParameter speedDown = new FloatParameter (1f);
|
||||
|
||||
|
||||
public bool IsActive() => filtering.overrideState;
|
||||
|
||||
public bool IsTileCompatible()
|
||||
{
|
||||
|
||||
@ -0,0 +1,184 @@
|
||||
#pragma warning(disable : 3568)
|
||||
|
||||
#pragma kernel KAutoExposureAvgLuminance_fixed MAIN=KAutoExposureAvgLuminance_fixed
|
||||
#pragma kernel KAutoExposureAvgLuminance_progressive MAIN=KAutoExposureAvgLuminance_progressive PROGRESSIVE
|
||||
|
||||
#pragma enable_d3d11_debug_symbols
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
#define EPSILON 1.0e-4
|
||||
|
||||
#define HISTOGRAM_BINS 128
|
||||
#define HISTOGRAM_TEXELS HISTOGRAM_BINS / 4
|
||||
|
||||
#if SHADER_API_GLES3 || SHADER_API_METAL
|
||||
#define HISTOGRAM_THREAD_X 8
|
||||
#define HISTOGRAM_THREAD_Y 8
|
||||
#define HISTOGRAM_REDUCTION_THREAD_X 8
|
||||
#define HISTOGRAM_REDUCTION_THREAD_Y 8
|
||||
#define HISTOGRAM_REDUCTION_ALT_PATH 1
|
||||
#else
|
||||
#define HISTOGRAM_THREAD_X 16
|
||||
#define HISTOGRAM_THREAD_Y 16
|
||||
#define HISTOGRAM_REDUCTION_THREAD_X HISTOGRAM_THREAD_X
|
||||
#define HISTOGRAM_REDUCTION_THREAD_Y HISTOGRAM_BINS / HISTOGRAM_THREAD_Y
|
||||
#define HISTOGRAM_REDUCTION_ALT_PATH 0
|
||||
#endif
|
||||
|
||||
#define HISTOGRAM_REDUCTION_BINS HISTOGRAM_REDUCTION_THREAD_X * HISTOGRAM_REDUCTION_THREAD_Y
|
||||
|
||||
half Luminance(half3 linearRgb)
|
||||
{
|
||||
return dot(linearRgb, float3(0.2126729, 0.7151522, 0.0721750));
|
||||
}
|
||||
|
||||
half Luminance(half4 linearRgba)
|
||||
{
|
||||
return Luminance(linearRgba.rgb);
|
||||
}
|
||||
|
||||
float GetHistogramBinFromLuminance(float value, float2 scaleOffset)
|
||||
{
|
||||
return saturate(log2(value) * scaleOffset.x + scaleOffset.y);
|
||||
}
|
||||
|
||||
float GetLuminanceFromHistogramBin(float bin, float2 scaleOffset)
|
||||
{
|
||||
return exp2((bin - scaleOffset.y) / scaleOffset.x);
|
||||
}
|
||||
|
||||
float GetBinValue(StructuredBuffer<uint> buffer, uint index, float maxHistogramValue)
|
||||
{
|
||||
return float(buffer[index]) * maxHistogramValue;
|
||||
}
|
||||
|
||||
float FindMaxHistogramValue(StructuredBuffer<uint> buffer)
|
||||
{
|
||||
uint maxValue = 0u;
|
||||
|
||||
for (uint i = 0; i < HISTOGRAM_BINS; i++)
|
||||
{
|
||||
uint h = buffer[i];
|
||||
maxValue = max(maxValue, h);
|
||||
}
|
||||
|
||||
return float(maxValue);
|
||||
}
|
||||
|
||||
void FilterLuminance(StructuredBuffer<uint> buffer, uint i, float maxHistogramValue, float2 scaleOffset, inout float4 filter)
|
||||
{
|
||||
float binValue = GetBinValue(buffer, i, maxHistogramValue);
|
||||
|
||||
// Filter dark areas
|
||||
float offset = min(filter.z, binValue);
|
||||
binValue -= offset;
|
||||
filter.zw -= offset.xx;
|
||||
|
||||
// Filter highlights
|
||||
binValue = min(filter.w, binValue);
|
||||
filter.w -= binValue;
|
||||
|
||||
// Luminance at the bin
|
||||
float luminance = GetLuminanceFromHistogramBin(float(i) / float(HISTOGRAM_BINS), scaleOffset);
|
||||
|
||||
filter.xy += float2(luminance * binValue, binValue);
|
||||
}
|
||||
|
||||
float GetAverageLuminance(StructuredBuffer<uint> buffer, float4 params, float maxHistogramValue, float2 scaleOffset)
|
||||
{
|
||||
// Sum of all bins
|
||||
uint i;
|
||||
float totalSum = 0.0;
|
||||
|
||||
UNITY_UNROLL
|
||||
|
||||
for (i = 0; i < HISTOGRAM_BINS; i++)
|
||||
totalSum += GetBinValue(buffer, i, maxHistogramValue);
|
||||
|
||||
// Skip darker and lighter parts of the histogram to stabilize the auto exposure
|
||||
// x: filtered sum
|
||||
// y: accumulator
|
||||
// zw: fractions
|
||||
float4 filter = float4(0.0, 0.0, totalSum * params.xy);
|
||||
|
||||
UNITY_UNROLL
|
||||
|
||||
for (i = 0; i < HISTOGRAM_BINS; i++)
|
||||
FilterLuminance(buffer, i, maxHistogramValue, scaleOffset, filter);
|
||||
|
||||
// Clamp to user brightness range
|
||||
return clamp(filter.x / max(filter.y, EPSILON), params.z, params.w);
|
||||
}
|
||||
|
||||
|
||||
StructuredBuffer<uint> _HistogramBuffer;
|
||||
Texture2D<float> _Source;
|
||||
RWTexture2D<float> _Destination;
|
||||
|
||||
float4 _Params1; // x: lowPercent, y: highPercent, z: minBrightness, w: maxBrightness
|
||||
float4 _Params2; // x: speed down, y: speed up, z: exposure compensation, w: delta time
|
||||
float4 _ScaleOffsetRes; // x: scale, y: offset, w: histogram pass width, h: histogram pass height
|
||||
|
||||
groupshared uint gs_pyramid[HISTOGRAM_REDUCTION_BINS];
|
||||
|
||||
float GetExposureMultiplier(float avgLuminance)
|
||||
{
|
||||
avgLuminance = max(EPSILON, avgLuminance);
|
||||
//float keyValue = 1.03 - (2.0 / (2.0 + log2(avgLuminance + 1.0)));
|
||||
float keyValue = _Params2.z;
|
||||
float exposure = keyValue / avgLuminance;
|
||||
return exposure;
|
||||
}
|
||||
|
||||
float InterpolateExposure(float newExposure, float oldExposure)
|
||||
{
|
||||
float delta = newExposure - oldExposure;
|
||||
float speed = delta > 0.0 ? _Params2.x : _Params2.y;
|
||||
float exposure = oldExposure + delta * (1.0 - exp2(-_Params2.w * speed));
|
||||
return exposure;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[numthreads(HISTOGRAM_REDUCTION_THREAD_X, HISTOGRAM_REDUCTION_THREAD_Y, 1)]
|
||||
void MAIN(uint2 groupThreadId : SV_GroupThreadID)
|
||||
{
|
||||
#if HISTOGRAM_REDUCTION_ALT_PATH
|
||||
const uint thread_id = groupThreadId.y * HISTOGRAM_REDUCTION_THREAD_X + groupThreadId.x;
|
||||
gs_pyramid[thread_id] = max(_HistogramBuffer[thread_id], _HistogramBuffer[thread_id + HISTOGRAM_REDUCTION_BINS]);
|
||||
#else
|
||||
const uint thread_id = groupThreadId.y * HISTOGRAM_REDUCTION_THREAD_X + groupThreadId.x;
|
||||
gs_pyramid[thread_id] = _HistogramBuffer[thread_id];
|
||||
#endif
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// Parallel reduction to find the max value
|
||||
UNITY_UNROLL
|
||||
|
||||
for (uint i = HISTOGRAM_REDUCTION_BINS >> 1u; i > 0u; i >>= 1u)
|
||||
{
|
||||
if (thread_id < i)
|
||||
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + i]);
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
}
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
if (thread_id == 0u)
|
||||
{
|
||||
float maxValue = 1.0 / float(gs_pyramid[0]);
|
||||
|
||||
#if PROGRESSIVE
|
||||
float avgLuminance = GetAverageLuminance(_HistogramBuffer, _Params1, maxValue, _ScaleOffsetRes.xy);
|
||||
float exposure = GetExposureMultiplier(avgLuminance);
|
||||
float prevExposure = _Source[uint2(0u, 0u)].x;
|
||||
exposure = InterpolateExposure(exposure, prevExposure);
|
||||
_Destination[uint2(0u, 0u)].x = exposure.x;
|
||||
#else
|
||||
float avgLuminance = GetAverageLuminance(_HistogramBuffer, _Params1, maxValue, _ScaleOffsetRes.xy);
|
||||
float exposure = GetExposureMultiplier(avgLuminance);
|
||||
_Destination[uint2(0u, 0u)].x = exposure.x;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -21,66 +21,14 @@ Shader "XRP/AutoExposure"
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
SamplerState sampler_BlitTexture; // Screen texture
|
||||
|
||||
float _MaxBrightness; // Limit the final brightness to this value
|
||||
float _Exposure; // The average exposure to work with
|
||||
float _WhitePoint; // White point for reinhard tonemapping
|
||||
float2 _Range;
|
||||
// Min and Max luminosity change, how much can each pixel be offset from it's original luminosity
|
||||
|
||||
// Converts from RGB space to Yxy (float3.x is Luminance)
|
||||
float3 convert_rgb_to_Yxy(float3 rgb)
|
||||
TEXTURE2D_X(_SourceTexture);
|
||||
TEXTURE2D_X(_AutoExposureTexture);
|
||||
|
||||
half GammaCorrect_half(half linearColor)
|
||||
{
|
||||
float3 xyz;
|
||||
xyz.x = dot(rgb, float3(0.4124f, 0.3576f, 0.1805f));
|
||||
xyz.y = dot(rgb, float3(0.2126f, 0.7152f, 0.0722f));
|
||||
xyz.z = dot(rgb, float3(0.0193f, 0.1192f, 0.9505f));
|
||||
|
||||
float sum = xyz.x + xyz.y + xyz.z;
|
||||
|
||||
float x = (sum > 0.0) ? (xyz.x / sum) : 0.0;
|
||||
float y = (sum > 0.0) ? (xyz.y / sum) : 0.0;
|
||||
|
||||
return float3(xyz.y, x, y); // Y, x, y
|
||||
return pow(linearColor,1/2.2);
|
||||
}
|
||||
|
||||
// Converts from Yxy (float3.x is Luminance) space to RGB
|
||||
float3 convert_Yxy_to_rgb(float3 Yxy)
|
||||
{
|
||||
float Y = Yxy.x;
|
||||
float x = Yxy.y;
|
||||
float y = Yxy.z;
|
||||
|
||||
float X = y > 0.0 ? x * Y / y : 0.0;
|
||||
float Z = y > 0.0 ? (1.0 - x - y) * Y / y : 0.0;
|
||||
|
||||
float3 XYZ = float3(X, Y, Z);
|
||||
|
||||
float3x3 M_XYZ2RGB = float3x3(
|
||||
3.2406f, -1.5372f, -0.4986f,
|
||||
-0.9689f, 1.8758f, 0.0415f,
|
||||
0.0557f, -0.2040f, 1.0570f
|
||||
);
|
||||
|
||||
return mul(M_XYZ2RGB, XYZ);
|
||||
}
|
||||
|
||||
// Tone Mapping function
|
||||
float reinhard2(float lp, float wp)
|
||||
{
|
||||
return lp * (1.0f + lp / (wp * wp)) / (1.0f + lp);
|
||||
}
|
||||
|
||||
// Sample texture and return RGB and A in separate variables
|
||||
void sample_split(float2 uv, out float3 rgb, out float alpha)
|
||||
{
|
||||
float4 color = _BlitTexture.SampleBias(sampler_BlitTexture, uv, _GlobalMipBias.x);;
|
||||
rgb = color.rgb;
|
||||
alpha = color.a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Varyings vert(uint vertexID: SV_VertexID)
|
||||
{
|
||||
Varyings o;
|
||||
@ -88,28 +36,16 @@ Shader "XRP/AutoExposure"
|
||||
o.texcoord = GetFullScreenTriangleTexCoord(vertexID);
|
||||
return o;
|
||||
}
|
||||
|
||||
// Fragment shader, adaptation of: https://bruop.github.io/tonemapping/
|
||||
|
||||
float4 frag(Varyings input) : SV_Target
|
||||
{
|
||||
float3 rgb;
|
||||
float alpha;
|
||||
sample_split(input.texcoord, rgb, alpha);
|
||||
|
||||
// Yxy.x is Y, the luminance
|
||||
float3 Yxy = convert_rgb_to_Yxy(rgb);
|
||||
Yxy.x = min(Yxy.x, _MaxBrightness);
|
||||
|
||||
// Tone mapping
|
||||
float lp = Yxy.x / (9.6f * _Exposure + 0.0001f);
|
||||
float new_lum = reinhard2(lp, _WhitePoint);
|
||||
|
||||
// Clamp the added luminosity and convert back to RGB
|
||||
Yxy.x += clamp(new_lum - Yxy.x, _Range.x, _Range.y);
|
||||
rgb = convert_Yxy_to_rgb(Yxy);
|
||||
|
||||
return float4(rgb, alpha);
|
||||
float2 uv = input.texcoord.xy;
|
||||
half4 color = SAMPLE_TEXTURE2D_X_LOD(_SourceTexture, sampler_LinearClamp, uv, _BlitMipLevel);
|
||||
float luminance = GammaCorrect_half(SAMPLE_TEXTURE2D_X_LOD(_AutoExposureTexture, sampler_LinearRepeat, float2(0,0), _BlitMipLevel).x) ;
|
||||
|
||||
return color * luminance;
|
||||
}
|
||||
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
#pragma kernel Main KERNEL_SCAN
|
||||
|
||||
// 32 or 64 should be ok
|
||||
#define THREAD_COUNT 32
|
||||
|
||||
// I/O
|
||||
uint2 _Dims; // Screen Dimensions
|
||||
Texture2D _Source; // Screen Texture
|
||||
RWStructuredBuffer<float> _OutBuffer; // Compute Group Output
|
||||
|
||||
// Shared row values
|
||||
groupshared float rows_lum[THREAD_COUNT];
|
||||
groupshared uint rows_pixels[THREAD_COUNT];
|
||||
|
||||
// Weird ass function for doing things with the luminosity, looks great on desmos
|
||||
float weird_ass_function(float x)
|
||||
{
|
||||
x += 0.1f;
|
||||
float w = 32.0f;
|
||||
return x * (x + w) / (w * x + 0.154f) - 0.137f;
|
||||
}
|
||||
|
||||
// Convert screen coordinates to the pixel's luminosity
|
||||
float get_screen_lum(uint x, uint y)
|
||||
{
|
||||
return dot(_Source[uint2(x, y)].rgb, float3(0.21267291f, 0.7151522f, 0.0721750f));
|
||||
}
|
||||
|
||||
// Each thread corresponds to a Y coordinate on the screen and is in charge of the whole row
|
||||
[numthreads(THREAD_COUNT, 1, 1)]
|
||||
void Main(uint group : SV_GroupID, uint thread : SV_GroupThreadID, uint y : SV_DispatchThreadID)
|
||||
{
|
||||
float lum_row = 0;
|
||||
uint pixels = 0;
|
||||
|
||||
// Get the total pixels and luminosity of each row
|
||||
if (y < _Dims.y)
|
||||
{
|
||||
for (uint x = 0; x < _Dims.x; x++)
|
||||
{
|
||||
pixels++;
|
||||
lum_row += get_screen_lum(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Store the results in the shared arrays
|
||||
rows_lum[thread] = lum_row;
|
||||
rows_pixels[thread] = pixels;
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// The thread id:0 of each workgroup is in charge of adding upp all the values of the threads in its group
|
||||
if (thread == 0)
|
||||
{
|
||||
float groupLumAvg = 0;
|
||||
uint groupPixels = 0;
|
||||
|
||||
// Add upp everything
|
||||
for (int row = 0; row < THREAD_COUNT; row++)
|
||||
{
|
||||
groupLumAvg += rows_lum[row];
|
||||
groupPixels += rows_pixels[row];
|
||||
}
|
||||
|
||||
// Store the group's luminosity and pixel count in the output buffer
|
||||
_OutBuffer[group * 2] = groupLumAvg;
|
||||
_OutBuffer[group * 2 + 1] = groupPixels;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
#pragma warning(disable : 3568)
|
||||
|
||||
#pragma kernel KEyeHistogram
|
||||
#pragma kernel KEyeHistogram USE_VIGNETTE_WEIGHTING
|
||||
#pragma kernel KEyeHistogramClear
|
||||
#pragma enable_d3d11_debug_symbols
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
#define EPSILON 1.0e-4
|
||||
|
||||
#define HISTOGRAM_BINS 128
|
||||
#define HISTOGRAM_TEXELS HISTOGRAM_BINS / 4
|
||||
|
||||
#if SHADER_API_GLES3 || SHADER_API_METAL
|
||||
#define HISTOGRAM_THREAD_X 8
|
||||
#define HISTOGRAM_THREAD_Y 8
|
||||
#define HISTOGRAM_REDUCTION_THREAD_X 8
|
||||
#define HISTOGRAM_REDUCTION_THREAD_Y 8
|
||||
#define HISTOGRAM_REDUCTION_ALT_PATH 1
|
||||
#else
|
||||
#define HISTOGRAM_THREAD_X 16
|
||||
#define HISTOGRAM_THREAD_Y 16
|
||||
#define HISTOGRAM_REDUCTION_THREAD_X HISTOGRAM_THREAD_X
|
||||
#define HISTOGRAM_REDUCTION_THREAD_Y HISTOGRAM_BINS / HISTOGRAM_THREAD_Y
|
||||
#define HISTOGRAM_REDUCTION_ALT_PATH 0
|
||||
#endif
|
||||
|
||||
#define HISTOGRAM_REDUCTION_BINS HISTOGRAM_REDUCTION_THREAD_X * HISTOGRAM_REDUCTION_THREAD_Y
|
||||
|
||||
half Luminance(half3 linearRgb)
|
||||
{
|
||||
return dot(linearRgb, float3(0.2126729, 0.7151522, 0.0721750));
|
||||
}
|
||||
|
||||
half Luminance(half4 linearRgba)
|
||||
{
|
||||
return Luminance(linearRgba.rgb);
|
||||
}
|
||||
float GetHistogramBinFromLuminance(float value, float2 scaleOffset)
|
||||
{
|
||||
return saturate(log2(value) * scaleOffset.x + scaleOffset.y);
|
||||
}
|
||||
|
||||
float GetLuminanceFromHistogramBin(float bin, float2 scaleOffset)
|
||||
{
|
||||
return exp2((bin - scaleOffset.y) / scaleOffset.x);
|
||||
}
|
||||
|
||||
float GetBinValue(StructuredBuffer<uint> buffer, uint index, float maxHistogramValue)
|
||||
{
|
||||
return float(buffer[index]) * maxHistogramValue;
|
||||
}
|
||||
|
||||
RWStructuredBuffer<uint> _HistogramBuffer;
|
||||
Texture2D<float4> _Source;
|
||||
SamplerState sampler_LinearClamp;
|
||||
|
||||
cbuffer Params
|
||||
{
|
||||
float4 _ScaleOffsetRes; // x: scale, y: offset, z: width, w: height
|
||||
};
|
||||
|
||||
groupshared uint gs_histogram[HISTOGRAM_BINS];
|
||||
|
||||
|
||||
[numthreads(HISTOGRAM_THREAD_X, HISTOGRAM_THREAD_Y, 1)]
|
||||
void KEyeHistogram(uint2 dispatchThreadId : SV_DispatchThreadID, uint2 groupThreadId : SV_GroupThreadID)
|
||||
{
|
||||
const uint localThreadId = groupThreadId.y * HISTOGRAM_THREAD_X + groupThreadId.x;
|
||||
|
||||
// Clears the shared memory
|
||||
#if HISTOGRAM_REDUCTION_ALT_PATH
|
||||
uint localThreadIdOff = localThreadId << 1u;
|
||||
if (localThreadIdOff < HISTOGRAM_BINS)
|
||||
{
|
||||
gs_histogram[localThreadIdOff ] = 0u;
|
||||
gs_histogram[localThreadIdOff + 1] = 0u;
|
||||
}
|
||||
#else
|
||||
if (localThreadId < HISTOGRAM_BINS)
|
||||
{
|
||||
gs_histogram[localThreadId] = 0u;
|
||||
}
|
||||
#endif
|
||||
|
||||
float2 ipos = float2(dispatchThreadId) * 2.0;
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// Gather local group histogram
|
||||
if (ipos.x < _ScaleOffsetRes.z && ipos.y < _ScaleOffsetRes.w)
|
||||
{
|
||||
uint weight = 1u;
|
||||
float2 sspos = ipos / _ScaleOffsetRes.zw;
|
||||
|
||||
// Vignette weighting to put more focus on what's in the center of the screen
|
||||
#if USE_VIGNETTE_WEIGHTING
|
||||
{
|
||||
float2 d = abs(sspos - (0.5).xx);
|
||||
float vfactor = saturate(1.0 - dot(d, d));
|
||||
vfactor *= vfactor;
|
||||
weight = (uint)(64.0 * vfactor);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 color = _Source.SampleLevel(sampler_LinearClamp, sspos, 0.0).xyz; // Bilinear downsample 2x
|
||||
float luminance = Luminance(color);
|
||||
float logLuminance = GetHistogramBinFromLuminance(luminance, _ScaleOffsetRes.xy);
|
||||
uint idx = (uint) (logLuminance * (HISTOGRAM_BINS - 1u));
|
||||
InterlockedAdd(gs_histogram[idx], weight);
|
||||
}
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// Merge everything
|
||||
#if HISTOGRAM_REDUCTION_ALT_PATH
|
||||
if (localThreadIdOff < HISTOGRAM_BINS)
|
||||
{
|
||||
InterlockedAdd(_HistogramBuffer[localThreadIdOff ], gs_histogram[localThreadIdOff ]);
|
||||
InterlockedAdd(_HistogramBuffer[localThreadIdOff + 1], gs_histogram[localThreadIdOff + 1]);
|
||||
}
|
||||
#else
|
||||
if (localThreadId < HISTOGRAM_BINS)
|
||||
{
|
||||
InterlockedAdd(_HistogramBuffer[localThreadId], gs_histogram[localThreadId]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[numthreads(HISTOGRAM_THREAD_X, 1, 1)]
|
||||
void KEyeHistogramClear(uint dispatchThreadId : SV_DispatchThreadID)
|
||||
{
|
||||
if (dispatchThreadId < HISTOGRAM_BINS)
|
||||
_HistogramBuffer[dispatchThreadId] = 0u;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b140e27dc74a1fb4d9ae30f8566b8919
|
||||
ComputeShaderImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
x
Reference in New Issue
Block a user