图形学基础|泛光(Bloom)
图形学基础|泛光(Bloom)
文章目录
- 图形学基础|泛光(Bloom)
- 一、前言
- 二、Bloom概述
- 2.1 亮度提取
- 2.2 模糊
- 2.2.1 高斯模糊
- 2.2.2 可分离滤波
- 2.2.3 双重模糊
- 2.3 叠加
- 2.4 小结
- 三、虚幻Bloom
- 参考博文
一、前言
泛光(Bloom) 效果会产生从图像明亮区域边界向外延伸的光线条纹,给人的感觉是极其明亮的光线压制住了摄像机。
这是一种现实世界中的光现象,通过它能够以较为适度的渲染性能成本极大地增强渲染图像的真实感。通常在后处理阶段实现。
泛光效果图:
二、Bloom概述
Bloom的实现非常简单,共分为三个步骤:
- 通过一个阈值,对需要处理的图像经过亮度提取;
- 对经过亮度提取的图像进行模糊处理;
- 叠加原图和模糊处理后的图像;
整个过程的示意图如下所示:
2.1 亮度提取
亮度提取很简单,首先给出标准的亮度公式:
Y=(0.299⋅R)+(0.587⋅G)+(0.114⋅B)Y=(0.299 \cdot R)+(0.587 \cdot G)+(0.114 \cdot B)Y=(0.299⋅R)+(0.587⋅G)+(0.114⋅B)
为便于计算,快速算法不采用标准公式,而使用以下只取小数点后两个有效位的近似公式:
Y=(0.3⋅R)+(0.59⋅G)+(0.11⋅B)Y=(0.3 \cdot R)+(0.59 \cdot G)+(0.11 \cdot B)Y=(0.3⋅R)+(0.59⋅G)+(0.11⋅B)
用代码表示即:
float Luminance(float3 Linear)
{return dot(Linear, float3(0.3, 0.59, 0.11));
}
亮度提取成功之后我们可以通过一个阙值来控制,并且把值限制在0-1范围内。
具体的Shader实现如下:
RWTexture2D<float3> BloomResult : register(u0);
Texture2D SceneColorTexture : register(t0);SamplerState LinearSampler : register(s0);void GetSampleUV(uint2 ScreenCoord, inout float2 UV, inout float2 HalfPixelSize)
{float2 ScreenSize;BloomResult.GetDimensions(ScreenSize.x, ScreenSize.y);float2 InvScreenSize = rcp(ScreenSize);HalfPixelSize = 0.5 * InvScreenSize;UV = ScreenCoord * InvScreenSize + HalfPixelSize;
}[numthreads(8, 8, 1)]
void CS_ExtractBloom(uint3 DispatchThreadID : SV_DispatchThreadID)
{float2 HalfPixelSize, UV;GetSampleUV(DispatchThreadID.xy, UV, HalfPixelSize);float3 Color = SceneColorTexture.SampleLevel(LinearSampler, UV, 0).xyz;// clamp to avoid artifacts from exceeding fp16 through framebuffer blending of multiple very bright lightsColor.rgb = min(float3(256 * 256, 256 * 256, 256 * 256), Color.rgb);half TotalLuminance = Luminance(Color);half BloomLuminance = TotalLuminance - BloomThreshold;// clamp to [0,1]half BloomAmount = saturate(BloomLuminance * 0.5f);BloomResult[DispatchThreadID.xy] = BloomAmount * Color;
}
2.2 模糊
在介绍本文泛光使用的具体模糊算法之前,会先介绍一下高斯模糊,以及可分离滤波。
这个两个概念会在以后涉及到的图形算法中有广泛的应用。
在这之后,将介绍本文具体使用的算法——双重模糊(Dual Blur)。
2.2.1 高斯模糊
图像与滤波中提到:
- 从数字信号处理的角度看,图像其实是一种波,可以用波的算法处理图像。
- 色彩剧烈变化的地方,就是图像的高频区域;色彩稳定平滑的地方,就是低频区域。
而图像模糊的本质一个过滤高频信号,保留低频信号的过程。
过滤高频的信号的一个常见可选方法是卷积滤波,以理解成每一个像素都取周边像素的平均值。
如下图所示,中间点和周围的点都取权重为1,进行求平均值,这是最简单的均值模糊。
在数值上,这是一种"平滑化"。在图形上,就相当于产生"模糊"效果,"中间点"失去细节。
如果使用简单平均,显然不是很合理,因为图像都是连续的,越靠近的点关系越密切,越远离的点关系越疏远。
因此,加权平均更合理,距离越近的点权重越大,距离越远的点权重越小。
正态分布显然是一种可取的权重分配模式。
在图形上,正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。
上面的正态分布是一维的,图像都是二维的,所以我们需要二维的正态分布。
假定中心点的坐标是(0,0),那么距离它最近的8个点的坐标如下:
更远的点以此类推。
为了计算权重矩阵,需要设定σ的值。假定σ=1.5,则模糊半径为1的权重矩阵如下:
这9个点的权重总和等于0.4787147,如果只计算这9个点的加权平均,还必须让它们的权重之和等于1。
因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵。
以上就是高斯模糊的权重矩阵生成过程。
那么计算高斯模糊,只需要像素值乘以对应的权重系数,再将各个结果进行叠加即可。
2.2.2 可分离滤波
图像滤波,在原图像上移动滤波器的过程中每一次的计算结果都不会影响到后面过程的计算结果。
因此,这是一个并行的算法,可以在GPU中进行实现,极大地加快图像滤波的处理速度。
当权重矩阵满足线性可分(Linearly separable) 性质,可以将滤波拆解为两个方向的滤波。
[x1y1x2y1x3y1x1y2x2y2x3y2x1y3x2y3x3y3]=[y1y2y3]∗[x1x2x3]\begin{bmatrix} x_1y_1 & x_2y_1 & x_3y_1 \\ x_1y_2 & x_2y_2 & x_3y_2 \\ x_1y_3 & x_2y_3 & x_3y_3 \\ \end{bmatrix} = \begin{bmatrix} y_1 \\ y_2 \\ y_3 \\ \end{bmatrix} \ast \begin{bmatrix} x_1 & x_2 & x_3\\ \end{bmatrix}⎣⎡x1y1x1y2x1y3x2y1x2y2x2y3x3y1x3y2x3y3⎦⎤=⎣⎡y1y2y3⎦⎤∗[x1x2x3]
使用左侧的权重矩阵进行滤波和右侧先进行X方向滤波再进行Y方向滤波(顺序可以互换),二者结果都是相同的。
高斯模糊就是一种满足线性可分的,下图展示了一个Gaussian Kernel的线性分解过程:
通过这样的方式,可以降低纹理采样的数量,降低复杂度。
原本所需要的的采样数量为:
M×N×m×nM\times N \times m \times nM×N×m×n
经过优化只需要:
M×N×m+M×N×nM \times N \times m + M \times N \times nM×N×m+M×N×n
其中,M和N是需要进行滤波的图像的维数,m和n是滤波器的维数。
不过,若采样分离滤波,则需要多使用一张临时纹理。
2.2.3 双重模糊
图像模糊算法在后处理渲染领域中占据着重要的地位。
高品质后处理:十种图像模糊算法的总结与实现对十种模糊算法进行总结、对比和盘点。
模糊算法的优劣,决定了后处理管线最终的渲染品质和消耗性能的多少。
文中提到了要评判一种模糊算法的好坏,主要有三个标准:
- 模糊品质(Quality) 。模糊品质的好坏是模糊算法是否优秀的主要指标。
- 模糊稳定性(Stability) 。模糊的稳定性决定了在画面变化过程中,模糊是否稳定,不会出现跳变或者闪烁。
- 性能(Performance) 。性能的好坏是模糊算法是否能被广泛使用的关键所在。
并给出十种图像模糊算法横向对比:
其中,双重模糊(Dual Blur)在三项指标均位于最佳,因而本文泛光中所需要的模糊采用该种模糊算法进行实现。
Dual Blur衍生于生于Kawase Filter,但是Kawase filter需要在相同分辨率下作乒乓模糊。
而Dual-Filter通过下采样和上采样实现。Dual filter对下采样和上采样通道使用不同的滤波内核,但采样距离是恒定的。
下采样滤波器通过对覆盖目标像素的四个像素进行采样来工作。
对角上的四个样本进行采样,以便从所有相邻像素中涂抹一些信息。这使得它总共有5个样本。
上采样滤波器通过从下采样过程中重建信息来工作。
选择这种图案是为了获得漂亮的光滑圆形。如果您想要更具艺术感的外观,也可以使用其他形状。还可以调整从目标像素到采样点的距离,以获得更大或更小的模糊。
具体的Shader实现如下:
[numthreads(8, 8, 1)]
void CS_DownSample(uint3 DispatchThreadID : SV_DispatchThreadID)
{float2 HalfPixelSize, UV;GetSampleUV(DispatchThreadID.xy, UV, HalfPixelSize);float3 Result = SceneColorTexture.SampleLevel(LinearSampler, UV, 0).xyz * 4.0;Result += SceneColorTexture.SampleLevel(LinearSampler, UV - HalfPixelSize, 0).xyz;Result += SceneColorTexture.SampleLevel(LinearSampler, UV + HalfPixelSize, 0).xyz;Result += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(HalfPixelSize.x, -HalfPixelSize.y), 0).xyz;Result += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(-HalfPixelSize.x, HalfPixelSize.y), 0).xyz;BloomResult[DispatchThreadID.xy] = Result / 8.0;
}[numthreads(8, 8, 1)]
void CS_UpSample(uint3 DispatchThreadID : SV_DispatchThreadID)
{float2 HalfPixelSize, UV;GetSampleUV(DispatchThreadID.xy, UV, HalfPixelSize);float3 Result = 0;Result += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(-HalfPixelSize.x * 2.0, 0.0), 0).xyz; //leftResult += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(+HalfPixelSize.x * 2.0, 0.0), 0).xyz; //rightResult += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(0.0, -HalfPixelSize.y * 2.0), 0).xyz; //upResult += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(0.0, +HalfPixelSize.y * 2.0), 0).xyz; //bottomResult += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(-HalfPixelSize.x, -HalfPixelSize.y), 0).xyz * 2.0; //top-leftResult += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(+HalfPixelSize.x, -HalfPixelSize.y), 0).xyz * 2.0; //top-rightResult += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(-HalfPixelSize.x, +HalfPixelSize.y), 0).xyz * 2.0; //bottom-leftResult += SceneColorTexture.SampleLevel(LinearSampler, UV + float2(+HalfPixelSize.x, +HalfPixelSize.y), 0).xyz * 2.0; //bottom-rightBloomResult[DispatchThreadID.xy] = Result / 12.0;
}
2.3 叠加
叠加这个过程就更简单了,就是将模糊后的图像和原图进行叠加。
混合的公式可以为:输出 = 原始图像 + 模糊图像 * bloom颜色 * bloom权值。
具体的Shader代码如下:
float4 PS_Merge(in VertexOutput Input) : SV_Target0
{float3 Color = SceneColorTexture.Sample(LinearSampler, Input.Tex).xyz;float3 Bloom = BloomTexture.Sample(LinearSampler, Input.Tex).xyz;return float4(Color + Bloom * BloomIntensity, 1.0);
}
2.4 小结
Bloom是后处理比较简单的一种效果。
下面给出笔者实现的效果示意图。
- 上边为没有开启Bloom的效果;
- 下边为开启了Bloom的效果;
三、虚幻Bloom
// TODO
参考博文
- 图像与滤波
- 高斯模糊
- Bloom-Effect-Unity
- Catlike-Bloom
- 高品质后处理:十种图像模糊算法的总结与实现
图形学基础|泛光(Bloom)相关推荐
- 图形学基础|抗锯齿(Anti-Aliasing)
图形学基础|抗锯齿(Anti-Aliasing) 文章目录 图形学基础|抗锯齿(Anti-Aliasing) 一.前言 二.锯齿 2.1 采样理论 2.2 分类 三.抗锯齿概述 3.1 SSAA(Su ...
- 计算机图形学基础考试题,计算机图形学基础复习题
<计算机图形学基础复习题>由会员分享,可在线阅读,更多相关<计算机图形学基础复习题(8页珍藏版)>请在人人文库网上搜索. 1.计算机图形学基础复习题 一.判断题 1. PNG( ...
- OpenGL 泛光Bloom
OpenGL 泛光Bloom 泛光Bloom简介 提取亮色 高斯模糊 把两个纹理混合 泛光Bloom简介 明亮的光源和区域经常很难向观察者表达出来,因为监视器的亮度范围是有限的.一种区分明亮光源的方式 ...
- 计算机图形学基础考试题及答案,计算机图形学基础模拟试题参考答案
1. 计算机图形学基础模拟试题参考答案一.名 词 解 释 ( 共 9 分 , 每 题 3 分 )1. 1. 计算机图形学研究怎样用计算机生成.处理和显示图形和科学.2构造根据选择的作图命令和指定的一系 ...
- 计算机图形学基础期末考试试题,计算机图形学基础_试卷(B)答案
计算机图形学 哈尔滨学院2006年秋季学期期末试卷 ( T )4.为了减少重复性工作一般均把常用图形的绘制设计成图形子程序. ( F )5.二维图形的基本变换后原图形的顶点没有改变. ( F )6.B ...
- 前端实现可绘制的canvas画布_前端图形学基础(五)——Canvas状态管理
点击右上角的关注,不定期前端干货分享!! 欢迎来到我的前端图形学系列文章: 前端图形学基础(一)--Canvas基础入门 前端图形学基础(二)--Canvas基础 前端图形学基础(三)--Canvas ...
- 计算机图形学基础教程论文,计算机图形学小论文
为毕业生写计算机图形学小论文提供计算机图形学小论文范文参考,涵盖硕士.大学本科毕业论文范文和职称论文范文,包括论文选题.开题报告.文献综述.任务书.参考文献等,是优秀免费计算机图形学小论文网站. 计算 ...
- 计算机图形学基础如何学,计算机图形学基础思考
计算机图形学基础思考 2019-08-07 版权声明 举报文章 第3届太平洋计算机图形学学术会议(PaeifieGraphies'95)于8月21日至24日在韩国汉城召开.会议邀请了专家作讲演,题目为 ...
- 计算机图形学孔令德基础知识,计算机图形学基础教程孔令德答案
计算机图形学基础教程孔令德答案 [篇一:大学计算机图形学课程设] 息科学与工程学院课程设计任务书 题目: 小组成员:巴春华.焦国栋 成员学号:专业班级:计算机科学与技术.2009级本2班课程: 计算机 ...
最新文章
- forward declaration of class 错误
- 'ModelOptions' object has no attribute 'get_field_names
- Eclipse 每行 79 字符限制的提示线
- 【laravel】开发过程中会遇到的问题
- spring事务管理-概念部分
- Git 常用命令(二)
- 稠密连接网络(DenseNet)
- 精选15个国外CSS框架
- Android一个Activity多个Handler时,Message消息是否会混乱?
- reg51.h和reg52.h头文件
- IBM:云存储三步走
- LeetCode 69. x的平方根
- 枚举类中的valueOf用法
- Android网络操作和优化相关
- USDT信用卡和转账入账接口
- 生鲜配送公司面临的几大痛点,你知道多少?
- swap函数 交换 vector 里面的两个元素
- python语言是 创造的_慢步python,如何用python语言创造出一个真正的独立exe程序?...
- 近年现场比赛补题(From 2013 to 2018)[持续更新]
- Python 基础数据结构