找回密码
 立即注册
查看: 204|回复: 0

图形渲染(四):后处理

[复制链接]
发表于 2023-3-24 17:24 | 显示全部楼层 |阅读模式
1.综述

后处理(PostProcessing),也称为数字图像处理,指将一张图片处理得到一张新的图片,简称P图,通常用在场景渲染结束后。深入了解这部分内容,可参考OpenCV教程,链接中列举了比较常见的后处理算法。
本文介绍3个后处理算法:伽马校正、色调映射、泛光。
2.伽马校正

伽马校正有2层含义,分别讲解。
2.1.CRT显示器

早期的CRT显示系统或采像设备存在的硬件特性指数,会使其输出较原始图像产生非线性失真,失真程度由具体系统的gamma值决定。如下图所示,首先电压和真实的图像亮度成正比例,水平方向为电压,垂直方向为显示设备的输出亮度,实线表示电压和输出亮度的关系。



CRT

output=voltage^\gamma=input^\gamma
其中,voltage是电压,input是真实图像亮度,output是CRT显示器输出亮度。
为了使显示设备的输出亮度或采像设备采集到的亮度与真实,我们可以对原始图像进行一次预补偿,让原始真实图像产生与硬件特性指数相反的反向失真,把反向失真作为CRT的输入:
voltage=input^{1/\gamma}
这样CRT的输出结果就和实际光亮相同了:
output=voltage^\gamma=input^{\frac{1}{\gamma}\cdot\gamma}=input
现如今CRT显示器早已过时,而主流的LCD显示器不存在非线性失真的问题,因此当有人提到伽马校正,一般指的是接下来要讲的含义。
2.2.颜色编码

人眼对不同亮度的敏感程度是不同的,对暗部的敏感要高于亮部(这个是没错的)。而存储颜色的空间是有限的,例如常用的 RGBA32 格式,每个颜色通道都只有 8 位,只能存储 256 种亮度,所以基于人眼感知的原因,用更多的空间存储更多暗部的颜色是更合理的。然而传统的颜色编码方式为linear RGB,各分量的值与光强成线性相关。



人眼对光强的敏感度

基于上面的分析,必须找到一种既符合人眼视觉特性,又节省编码空间的方法来进行编码,于是非线性编码就成了首选,SRGB是一种常用的非线性编码方式。gamma校正就是把线性编码转化为非线性编码的过程,转化函数如下:
C=I^\frac{1}{\gamma}
其中\gamma值为2.2。
3.色调映射

3.1.HDR

SDR全称是Standard Dynamic Range,意思是标准动态范围,SDR编码颜色深度通常以8bit为主。
HDR全称是High-Dynamic Range,意思是高动态范围,HDR编码颜色深度至少为10bit,因此与普通图像相比,HDR可以提供更多的色彩范围和图像细节,呈现极高的图像品质。
转换HDR值到SDR值的过程叫做色调映射(Tone Mapping)。Tone Mapping的过程就是首先要根据当前的场景推算出场景的平均亮度,再根据这个平均亮度选取一个合适的亮度域,再将整个场景映射到这个亮度域得到正确的结果。
色调映射的算法有很多,包括但不限于:Filmic Tone Mapping,ACES Tone mapping,Reinhard tone mapping。Reinhard tone mapping是早期用得比较广泛的色调映射算法,可以当做学习过程中的入门算法,下面介绍一下。
3.2.Reinhard tone mapping

该算法的运算流程参考下面的步骤。
3.2.1.计算平均亮度

首先计算出整个场景的平均亮度,计算平均亮度的方法有很多种,目前常用的是使用log-average亮度来作为场景的平均亮度,通过下面的公式可以计算得到:
\overline{L}_w=\frac{1}{N}\exp(\sum_{x,y}{\log{(\delta+L_w(x,y))}})
其中L_w(x,y)是像素点(x,y)的亮度,N是场景内的像素数,δ是一个很小的数用来应对像素点纯黑的情况,用于保证log函数的参数大于0,L_w(x,y)的求解公式为:
L_w(x,y)=0.30R+0.59G+0.11B
3.2.2.计算相对亮度

计算屏幕各个像素亮度相对于平均亮度的比值,并乘上一个系数\alpha,得到调整后的亮度值,这个系数\alpha有个专门的名称叫做key value,其取值范围为[0,1]。
L(x,y)=\frac{\alpha}{\overline{L_w}}L_w(x,y)
上面的公式用来计算每个像素相对于平均亮度的亮度,α用来控制场景的亮度倾向,一般来说,会使用几个特定的值,0.18是一个适中的亮度,0.36或者0.72相对偏亮,0.09甚至0.045则是偏暗。
3.2.3.映射到区间

计算机能显示的亮度范围为[0,1]区间,可以通过下面的公式把相对亮度映射到[0,1]区间:
L_d(x,y)=\frac{L(x,y)}{1+L(x,y)}
但是上面的显示效果不太好,建议用下面的公式:
L_d(x,y)=\frac{L(x,y)(1+\frac{L(x,y)}{L_{white}^2})}{1+L(x,y)}
参数L_{white}用来控制场景中的曝光,凡是亮度超过L_{white}的像素都会被置为纯白。
4.Bloom

泛光(Bloom) 效果会产生从图像明亮区域边界向外延伸的光线条纹,给人的感觉是极其明亮的光线压制住了摄像机。看下效果:



Bloom效果

此类效果的实现思路分3步:把需要泛光效果的区域提取出来;进行一次模糊;再叠加到原图上去。



Bloom流程

详细流程如下。
4.1.计算平均亮度

首先计算出整个场景的平均亮度,计算平均亮度的方法有很多种,目前常用的是使用log-average亮度来作为场景的平均亮度,通过下面的公式可以计算得到:
\overline{L}_w=\frac{1}{N}\exp(\sum_{x,y}{\log{(\delta+L_w(x,y))}})
其中L_w(x,y)是像素点(x,y)的亮度,N是场景内的像素数,δ是一个很小的数用来应对像素点纯黑的情况,用于保证log函数的参数大于0,L_w(x,y)的求解公式为:
L_w(x,y)=0.30R+0.59G+0.11B
4.2.计算相对亮度

计算屏幕各个像素亮度相对于平均亮度的比值,并乘上一个系数\alpha,得到调整后的亮度值,这个系数\alpha有个专门的名称叫做key value,其取值范围为[0,1]。
L(x,y)=\alpha\frac{L_w(x,y)}{\overline{L_w}}
上面的公式用来计算每个像素相对于平均亮度的亮度,α用来控制场景的亮度倾向,一般来说,会使用几个特定的值,0.18是一个适中的亮度,0.36或者0.72相对偏亮,0.09甚至0.045则是偏暗。
4.3.过滤

过滤出场景中的高亮区域,将调整后的亮度值与一个高亮阈值t做比对,将大于0的像素的颜色输出,小于0的像素输出数值为0,这一步的输出贴图中,除了高亮部分是点亮的之外,其余部分都是全黑的:
L_{bright}=\max(L_{scaled}-t,0)
4.4.卷积

对上一步输出的高光贴图进行高斯模糊,得到模糊后的高光贴图。计算方法是,选取每个像素点周围N*N个点,然后对颜色进行加权平均,把得到的结果作为该像素点的最终颜色:
C(x,y)=\sum_{N\times N}{w(x,y)C_0(x,y)}
像素点的权值w(x,y)计算公式如下:
w(x,y)=(\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{s^2+t^2}{2\sigma^2}})
其中(s,t)是周围像素点相对于(x,y)的相对坐标,相对坐标计算方法参考下图。



权值计算

4.5.混合

将模糊后的高光贴图跟原始颜色贴图进行混合,输出的结果就带有泛光效果了。
5.参考


  • https://blog.csdn.net/qjh5606/article/details/119005531
  • https://xiaoiver.github.io/coding/2019/02/05/HDR-Tone-Mapping.html
  • https://www.jianshu.com/p/f3b91707034a
  • https://blog.csdn.net/Quincuntial/article/details/50625389
  • https://zhuanlan.zhihu.com/p/105909416?ivk_sa=1024320u&utm_id=0
  • https://eizo.com.cn/global/library/management/ins-and-outs-of-hdr/index1.html
本文使用 Zhihu On VSCode 创作并发布

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-30 11:46 , Processed in 0.109157 second(s), 28 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表