屏幕自动亮度不停的变_【UE4ToUnity】自动曝光Histogram

本文介绍了自动曝光的概念及其与Tonemap的关系,重点对比了Unity和UE4中计算亮度直方图的方法,探讨了Unity的ComputeShader实现和UE4的复杂实现策略。通过计算亮度直方图来调整曝光值,以适应场景亮度变化,实现更加精确的自动曝光效果。文章最后提到了UE4的VisualHDR功能,用于在HDR空间下监控屏幕颜色。
摘要由CSDN通过智能技术生成

上一篇文章总结了UE4 中Tonemap,ColorGrade的实现,其实Unity PostProcess 中有着极为类似的实现,都是ACES....有兴趣的小伙伴可以看一下Unity 的实现,顺便可以和UE4 对比一下差异~

未名客:【UE4ToUnity】ColorGrade&Tonemap​zhuanlan.zhihu.com
19b168f9117d934ad0da3316883ea581.png

今天的主题是自动曝光。

一、背景概述

与Tonemap 最有关联的后处理便是自动曝光了,简单来说,Tonemap 的存在,即使我们对SceneColor 乘一个较大的值,再用Tonemap 矫正,都会得到一个比较平滑的结果。这个“值”就是简单的理解为曝光值。

如果当前屏幕亮度整体很暗,我们希望提高画面亮度,就可以给一个比较大的曝光值,假设屏幕整体很亮,希望降低亮度,可以给一个比较小的曝光值。自动曝光就是,根据场景SceneColor的亮度,自动计算一个曝光值,达到上述的需求。

那么问题来了,如何自动计算曝光值?

其实从上文的描述中就可以总结出来,自动曝光的曝光值与“SceneColor的整体亮度”有关,我们一般用平均亮度描述,或者说是中灰值。平均亮度的计算方法有很多,以前最常见的方法是,对SceneColor 做DownSample,一路DownSample 下来,最后得到一个1x1的颜色,这个颜色就是场景的中灰值。这种方法简单粗暴,但是并不准确,已经慢慢被遗弃,现在主流的做法,是计算场景亮度的柱状图Histogram,并根据柱状图求平均亮度,以求达到一个更为准确的结果。

二、计算亮度直方图

整体思路上UE4 和Unity的做法非常类似,但是也有不同,Unity的实现简洁明了,UE4 参杂了一些的“黑科技”,但是导致整体代码晦涩难懂。我先从Unity 这边总结整套算法的具体流程,然后会简单指出一些UE4 的做法,最后给出UE4 VisualHDR 的实现,也就是标题图,来结束这篇文章。

最核心都是要计算一个类似PS 中的明度直方图,只不过,为了性能考虑,不能有这么多的采样点。

bb1e22c4ac39ab88e1287cc8deea876f.png

首先,HDR 空间下的颜色范围是无限的,我们需要缩小这个范围,毕竟场景屏幕上的颜色大部分都是一个比较”合理的“可控的值。然后对这个颜色值求亮度,量化这个亮度,使它由连续变离散,确定有限采样次数,比如64次,这样我们就表示出了柱状图x轴的意义。

完成了上面的步骤,就可以遍历场景屏幕上的每一个像素,计算柱状图的Y轴,也就是说,对于x轴上的亮度,屏幕上有多少个相关的像素。

流程很简单,现在简单来看一下两个引擎的具体实现。

2.1 Unity 实现

因为比较懒,没有升级Unity,以及PostProcessSteck, 就一PPS Ver1 为例,来简单介绍一下吧。Shader的实现在下面这个文件里:

AssetsPostProcessingResourcesShadersEyeHistogram.compute

Script的实现在:

AssetsPostProcessingRuntimeComponentsEyeAdaptationComponent.cs

// Put the following line to 0 or comment it to disable vignette weighting
#define USE_VIGNETTE_WEIGHTING 1

#include "Common.cginc"
#include "EyeAdaptation.cginc"

RWStructuredBuffer<uint> _Histogram;
Texture2D<float4> _Source;

CBUFFER_START(Params)
    float4 _ScaleOffsetRes; // x: scale, y: offset, z: width, w: height
CBUFFER_END

groupshared uint gs_histogram[HISTOGRAM_BINS];

#pragma kernel KEyeHistogram
[numthreads(HISTOGRAM_THREAD_X,HISTOGRAM_THREAD_Y,1)]
void KEyeHistogram(uint2 dispatchThreadId : SV_DispatchThreadID, uint2 groupThreadId : SV_GroupThreadID)
{
    
    // Pretty straightforward implementation of histogram gathering using atomic ops.
    // I tried a few methods (no atomic ops / heavy LDS leveraging) but this one turned out to be
    // the fastest on desktop (Nvidia - Kepler/Maxwell) and PS4. Still need to try it on GCN/desktop
    // but considering it runs very fast on PS4 we can expect it to run well (?).

    const uint localThreadId = groupThreadId.y * HISTOGRAM_THREAD_X + groupThreadId.x;

    // Clears the shared memory
    if (
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值