public override void Render(PostProcessRenderContext context) { var cmd = context.command; cmd.BeginSample("AutoExposureLookup"); var sheet = context.propertySheets.Get(context.resources.shaders.autoExposure); sheet.ClearKeywords(); // Prepare autoExpo texture pool CheckTexture(0); CheckTexture(1); // Make sure filtering values are correct to avoid apocalyptic consequences float lowPercent = settings.filtering.value.x; float highPercent = settings.filtering.value.y; const float kMinDelta = 1e-2f; highPercent = Mathf.Clamp(highPercent, 1f + kMinDelta, 99f); lowPercent = Mathf.Clamp(lowPercent, 1f, highPercent - kMinDelta); // Compute auto exposure sheet.properties.SetBuffer(Uniforms._HistogramBuffer, context.logHistogram.data); sheet.properties.SetVector(Uniforms._Params, new Vector4(lowPercent * 0.01f, highPercent * 0.01f, RuntimeUtilities.Exp2(settings.minLuminance.value), RuntimeUtilities.Exp2(settings.maxLuminance.value))); sheet.properties.SetVector(Uniforms._Speed, new Vector2(settings.speedDown.value, settings.speedUp.value)); sheet.properties.SetVector(Uniforms._ScaleOffsetRes, context.logHistogram.GetHistogramScaleOffsetRes(context)); sheet.properties.SetFloat(Uniforms._ExposureCompensation, settings.keyValue.value); if (m_FirstFrame || !Application.isPlaying) { // We don't want eye adaptation when not in play mode because the GameView isn't // animated, thus making it harder to tweak. Just use the final audo exposure value. m_CurrentAutoExposure = m_AutoExposurePool[0]; cmd.BlitFullscreenTriangle(BuiltinRenderTextureType.None, m_CurrentAutoExposure, sheet, (int)EyeAdaptation.Fixed); // Copy current exposure to the other pingpong target to avoid adapting from black RuntimeUtilities.CopyTexture(cmd, m_AutoExposurePool[0], m_AutoExposurePool[1]); } else { int pp = m_AutoExposurePingPong; var src = m_AutoExposurePool[++pp % 2]; var dst = m_AutoExposurePool[++pp % 2]; cmd.BlitFullscreenTriangle(src, dst, sheet, (int)settings.eyeAdaptation.value); m_AutoExposurePingPong = ++pp % 2; m_CurrentAutoExposure = dst; } cmd.EndSample("AutoExposureLookup"); context.autoExposureTexture = m_CurrentAutoExposure; context.autoExposure = settings; m_FirstFrame = false; }
public override void Render(PostProcessRenderContext context) { // Setup compute if (m_EyeCompute == null) { m_EyeCompute = Resources.Load <ComputeShader>("Shaders/Builtins/ExposureHistogram"); } var cmd = context.command; cmd.BeginSample("AutoExposureLookup"); var sheet = context.propertySheets.Get("Hidden/PostProcessing/AutoExposure"); sheet.ClearKeywords(); if (m_HistogramBuffer == null) { m_HistogramBuffer = new ComputeBuffer(k_HistogramBins, sizeof(uint)); } // Downscale the framebuffer, we don't need an absolute precision for auto exposure and it // helps making it more stable var scaleOffsetRes = GetHistogramScaleOffsetRes(context); cmd.GetTemporaryRT(Uniforms._AutoExposureCopyTex, (int)scaleOffsetRes.z, (int)scaleOffsetRes.w, 0, FilterMode.Bilinear, context.sourceFormat); cmd.BlitFullscreenTriangle(context.source, Uniforms._AutoExposureCopyTex); // Prepare autoExpo texture pool CheckTexture(0); CheckTexture(1); // Clear the buffer on every frame as we use it to accumulate luminance values on each frame m_HistogramBuffer.SetData(s_EmptyHistogramBuffer); // Get a log histogram int kernel = m_EyeCompute.FindKernel("KEyeHistogram"); cmd.SetComputeBufferParam(m_EyeCompute, kernel, "_HistogramBuffer", m_HistogramBuffer); cmd.SetComputeTextureParam(m_EyeCompute, kernel, "_Source", Uniforms._AutoExposureCopyTex); cmd.SetComputeVectorParam(m_EyeCompute, "_ScaleOffsetRes", scaleOffsetRes); cmd.DispatchCompute(m_EyeCompute, kernel, Mathf.CeilToInt(scaleOffsetRes.z / (float)k_HistogramThreadX), Mathf.CeilToInt(scaleOffsetRes.w / (float)k_HistogramThreadY), 1); // Cleanup cmd.ReleaseTemporaryRT(Uniforms._AutoExposureCopyTex); // Make sure filtering values are correct to avoid apocalyptic consequences float lowPercent = settings.filtering.value.x; float highPercent = settings.filtering.value.y; const float kMinDelta = 1e-2f; highPercent = Mathf.Clamp(highPercent, 1f + kMinDelta, 99f); lowPercent = Mathf.Clamp(lowPercent, 1f, highPercent - kMinDelta); // Compute auto exposure sheet.properties.SetBuffer(Uniforms._HistogramBuffer, m_HistogramBuffer); sheet.properties.SetVector(Uniforms._Params, new Vector4(lowPercent * 0.01f, highPercent * 0.01f, RuntimeUtilities.Exp2(settings.minLuminance.value), RuntimeUtilities.Exp2(settings.maxLuminance.value))); sheet.properties.SetVector(Uniforms._Speed, new Vector2(settings.speedDown.value, settings.speedUp.value)); sheet.properties.SetVector(Uniforms._ScaleOffsetRes, scaleOffsetRes); sheet.properties.SetFloat(Uniforms._ExposureCompensation, settings.keyValue.value); if (settings.dynamicKeyValue) { sheet.EnableKeyword("AUTO_KEY_VALUE"); } if (m_FirstFrame || !Application.isPlaying) { // We don't want eye adaptation when not in play mode because the GameView isn't // animated, thus making it harder to tweak. Just use the final audo exposure value. m_CurrentAutoExposure = m_AutoExposurePool[0]; cmd.BlitFullscreenTriangle((Texture)null, m_CurrentAutoExposure, sheet, (int)EyeAdaptation.Fixed); // Copy current exposure to the other pingpong target to avoid adapting from black RuntimeUtilities.CopyTexture(cmd, m_AutoExposurePool[0], m_AutoExposurePool[1]); } else { int pp = m_AutoExposurePingPong; var src = m_AutoExposurePool[++pp % 2]; var dst = m_AutoExposurePool[++pp % 2]; cmd.BlitFullscreenTriangle(src, dst, sheet, (int)settings.eyeAdaptation.value); m_AutoExposurePingPong = ++pp % 2; m_CurrentAutoExposure = dst; } cmd.EndSample("AutoExposureLookup"); context.autoExposureTexture = m_CurrentAutoExposure; m_FirstFrame = false; }