哈Ha! 我向您介绍约翰·查普曼(John Chapman)撰写的文章
“ Pseudo Lens Flare”的翻译。
镜头光晕 (lens flare)是由光在镜头系统中的散射和折射引起的摄影伪影。 尽管这是伪影,但在计算机图形学中使用
镜头眩光有很多原因:
- 它增加了图像的感知亮度和可见动态范围。
- 镜头光斑经常出现在照片中,因此可能会令人惊讶
- 它可以在风格或戏剧中扮演重要角色,也可以在游戏中扮演游戏的一部分(想象眩光使玩家蒙蔽)
传统上,使用基于子画面的技术实时实现
镜头光晕 。 尽管精灵会提供易于控制且非常逼真的结果,但必须将其明确放置并需要遮挡数据才能正确显示。 在这里,我将描述一个简单且相对便宜的屏幕空间效果,该效果从输入颜色缓冲区中创建伪
镜头光晕 。 它不是基于物理学的,因此其结果与逼真的效果略有不同,但是可以与传统的基于精灵的效果结合使用(或替代)。
演算法
包括四个阶段:
- 下采样/阈值。
- 产生镜头眩光元素。
- 模糊
- 高档/与原始图像融合。
1.下采样/阈值
下采样 -优化以减少后续步骤的成本。 另外,我们要选择原始图像中最亮像素的子集。 使用
规模/偏差 (规模/偏差)可提供一种灵活的方法来实现此目标:
uniform sampler2D uInputTex; uniform vec4 uScale; uniform vec4 uBias; noperspective in vec2 vTexcoord; out vec4 fResult; void main() { fResult = max(vec4(0.0), texture(uInputTex, vTexcoord) + uBias) * uScale; }
缩放/偏差调整是调整效果的主要方法; 最佳设置将取决于颜色缓冲区的动态范围,以及要查看结果的厚度。 由于该技术是一种近似的事实,因此细微之处看起来更好。
2.产生镜头眩光元素
镜头光晕元素倾向于围绕图像中心旋转。 通过模拟这种效果,我们可以水平/垂直扩展前一阶段的结果。 通过扩展纹理坐标,在元素生成阶段很容易做到这一点:
vec2 texcoord = -vTexcoords + vec2(1.0);
这是没有必要的。 有无元素生成都可以正常工作。 但是,更改纹理坐标的结果有助于在视觉上将
镜头眩光效果与原始图像区分开。
鬼魂
“重影”(重影)是重复的高光,反映了色彩缓冲区中的明亮区域,相对于图像中心展开。 我选择生成的方法是从当前像素到屏幕中心获取一个矢量,然后沿着该矢量进行多个选择。

uniform sampler2D uInputTex; uniform int uGhosts;
注意,我使用
fract()确保纹理坐标环绕; 等效地,您可以将环绕模式
GL_REPEAT用于纹理。
结果如下:

您可以通过仅允许靠近图像中心的明亮区域生成重影来改善结果。 我们可以通过增加权重来实现这一点,权重将从样本的中心开始递减:
vec4 result = vec4(0.0); for (int i = 0; i < uGhosts; ++i) { vec2 offset = fract(texcoord + ghostVec * float(i)); float weight = length(vec2(0.5) - offset) / length(vec2(0.5)); weight = pow(1.0 - weight, 10.0); result += texture(uInputTex, offset) * weight; }
权重函数尽可能简单-线性。 我们在循环内计算权重的原因是,输入图像中心的明亮区域可以将重影“投射”到边界,但是边界处的明亮区域无法将重影投射到中心。

最终的改进是根据1D纹理,重影的径向颜色变化:

在循环之后应用它,以影响幻影的最终颜色:
result *= texture(uLensColor, length(vec2(0.5) - texcoord) / length(vec2(0.5)));
光环(光环)
如果像在
重影计算中那样将矢量移到图像的中心,但是固定矢量的长度,则会得到不同的效果:原始图像会发生径向变形:

我们可以使用它来通过将权重乘以样本来创建“光晕”,从而限制变形图像对半径受
uHaloWidth控制的环的
贡献 :

色度失真(色彩失真)
某些镜头光斑会因不同波长的光折射变化而导致色彩失真。 我们可以通过创建一个函数来模拟此情况,该函数分别选择沿样本矢量偏移量稍有不同的红色,绿色和蓝色通道:
vec3 textureDistorted( in sampler2D tex, in vec2 texcoord, in vec2 direction,
它可以用作上一个清单中调用
texture()的直接替换。 我计算
方向和
变形如下:
vec2 texelSize = 1.0 / vec2(textureSize(uInputTex, 0)); vec3 distortion = vec3(-texelSize.x * uDistortion, 0.0, texelSize.x * uDistortion); vec3 direction = normalize(ghostVec);
尽管获取功能很简单,但它会从纹理中提取x3个样本,尽管它们都应该是缓存友好的,除非您将
uDistortion设置为某个巨大的值。
随着元素的产生,一切。 结果如下:

3.模糊
在没有模糊的情况下,
镜头光晕元素(尤其是重影)倾向于保留图像的外观。 通过为
镜头光晕元素添加模糊效果,我们可以减弱高频,从而降低与输入图像的对比度,这有助于我们出售效果。

我不会告诉你如何模糊。 您可以在各种Internet资源上阅读它(高斯模糊)。
4.高档/与原始图像融合
因此,我们有模糊的
镜头光晕元素。 我们如何将它们与原始源图像结合在一起? 关于整个渲染管道,有几个重要的注意事项:
- 在与镜头光晕组合之前,必须先应用任何后续的运动模糊或景深 ,以使镜头光晕元素不会参与这些效果。
- 在进行任何色调映射之前,应先应用镜头光晕 。 这具有物理意义,因为色调映射模拟了胶片/ CMOS对入射光的响应,而镜头眩光是必不可少的一部分。
考虑到这一点,我们现阶段可以做一些事情来改善结果:
镜头污垢
首先,您需要以肮脏的纹理以全分辨率修改
镜头光晕元素(在《战地风云3》中广泛使用):

uniform sampler2D uInputTex;
关键在于镜片上非常脏的质地。 如果对比度低,则
镜头光晕形状倾向于主导结果。 随着对比度的增加,
镜头光晕元素被消隐,这赋予了不同的美学外观,并且还隐藏了一些缺陷。
衍射星爆
作为一项额外的改进,我们可以通过将
starburst纹理添加到
镜头污垢中来使用它:

作为纹理,
爆炸形看起来不太好。 不过,我们可以将转换矩阵传递给着色器,这将使我们能够在每帧中旋转/变形
爆炸,并获得所需的动态效果:
uniform sampler2D uInputTex;
uLensStarMatrix转换
矩阵基于从摄影机方向获得的值,如下所示:
vec3 camx = cam.getViewMatrix().col(0);
还有其他方法可以获取凸轮值。 最重要的是,当旋转相机时,它应该连续变化。 矩阵本身的构造如下:
mat3 scaleBias1 = ( 2.0f, 0.0f, -1.0f, 0.0f, 2.0f, -1.0f, 0.0f, 0.0f, 1.0f, ); mat3 rotation = ( cos(camrot), -sin(camrot), 0.0f, sin(camrot), cos(camrot), 0.0f, 0.0f, 0.0f, 1.0f ); mat3 scaleBias2 = ( 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, ); mat3 uLensStarMatrix = scaleBias2 * rotation * scaleBias1;
比例和
偏置矩阵需要纹理原点偏移,因此我们可以相对于图像中心旋转
爆炸形 。
结论
所以现在一切! 此方法演示了相对简化的后期处理如何使
镜头光晕看起来不错。 它不是完全真实的照片,但是如果使用正确,它可以产生出色的效果。
