XBImageFilters 1.1.2

XBImageFilters 1.1.2

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布最新版本2014年12月

xissburg维护。



XBImageFilters 允许您实时从任何图像或相机获取过滤版本。它使用了 OpenGL ES 2 来通过您自己编写的片段着色器对图像进行过滤,因此您可以以任何您想要的方式过滤您的图像,而且速度非常快。

Luminance

在这个示例截图中,顶部一半屏幕是一个普通的 UIImageView,其 contentMode 设置为 UIViewContentModeTop,底部一半是一个 XBFilteredImageView,使用相同图像,contentMode 设置为 UIViewContentModeBottom,并应用了一个输出像素颜色亮度的过滤 [一个 GLSL 片段着色器]。

Gaussian Blur

卷积高斯模糊,这是一个多路过滤的示例。它为此使用了两个片段着色器,VGaussianBlur.glsl 和 HGaussianBlur.glsl。首先,它使用 VGaussianBlur 来执行垂直模糊,并将结果图像存储在 OpenGL 纹理中,然后它使用 HGaussianBlur 在之前渲染的纹理中应用水平模糊。作为结果,我们得到了一个具有径向核的正确的高斯模糊。

Camera Filter

实时相机过滤。它还允许您使用 -[XBFilteredView takeScreenshot] 方法,在应用了过滤的情况下以 UIImage 形式拍照。

High Resolution Photo

您还可以拍摄带有过滤的高分辨率照片。这难道不令人兴奋吗?

如何使用

框架

您必须在项目中添加几个框架,以成功链接 XBImageFilters。

  • QuartzCore
  • CoreMedia
  • CoreVideo
  • OpenGLES
  • AVFoundation
  • GLKit

图像过滤

XBFilteredImageView 是一个由 OpenGL 层支持的视图,它在应用自定义着色器后显示图像。要使用它,创建一个 XBFilteredImageView 实例,设置其图像和一个或多个过滤器,并将其添加为子视图。查看 ImageViewController.m 以查看完整的示例。

XBFilteredImageView *filteredImageView = [[XBFilteredImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height];
filteredImageView.image = [UIImage imageNamed:@"raccoons"];
NSString *shaderPath = [[NSBundle mainBundle] pathForResource:@"SomeFilterFragmentShader" ofType:@"glsl"];
NSError *error = nil;
if (![filteredImageView setFilterFragmentShaderFromFile:shaderPath error:&error]) {
    NSLog(@"%@", [error localizedDescription]);
}
[self.view addSubview:filteredImageView];

当然,您也可以在 Interface Builder 中创建一个 XBFilteredImageView。只需创建一个 UIView,将其类设置为 XBFilteredImageView 并连接到 an IBOutlet。然后在视图控制器中,您可以在 viewDidLoad 中设置其着色器。

实时相机过滤

XBFilteredCameraView 是一个由 OpenGL 层支持的视图,它实时地在镜头后应用自定义着色器并显示图像。要使用它,创建一个 XBFilteredCameraView 实例,设置其过滤器,将其添加为子视图,并调用 -[XBFilteredCameraView startCapturing]。您可以在 CameraViewController.m 中找到一个完整的示例。

XBFilteredCameraView *filteredCameraView = [[XBFilteredCameraView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height];
NSString *shaderPath = [[NSBundle mainBundle] pathForResource:@"SomeFilterFragmentShader" ofType:@"glsl"];
NSError *error = nil;
if (![filteredCameraView setFilterFragmentShaderFromFile:shaderPath error:&error]) {
    NSLog(@"%@", [error localizedDescription]);
}
[self.view addSubview:filteredCameraView];
[filteredCameraView startCapturing];

在捕获过程中,您可以通过调用takeAPhotoWithCompletion:来抓取一张照片,该方法会在完成块中将经过滤镜处理的高清晰度UIImage返回给您。您还可以在Interface Builder中创建XBFilteredCameraViews。

编写滤镜

要编写自己的着色器,您需要对OpenGL架构和特别是GLSL语言有一个很好的理解,以及着色器是如何工作的。简而言之,片元着色器是一段在GPU上为每个像素运行的代码,并返回该像素的最终颜色。您可以看到您在着色器中定义的一些变量的值。您可以声明一个uniform变量(对所有像素都是常量)并从CPU上运行的代码中提供一个值给它,然后使用这个值来计算像素的最终颜色。使用uniform,您可以控制滤镜中的诸如模糊半径、运动模糊方向等参数。

严格来说,片元着色器并不是为每个像素运行的。屏幕上的每个像素可能不止有一个采样点。对于每个这样的采样点,都会运行片元着色器。所以,如果我们有4个采样点,那么片元着色器将为每个像素运行4次。但在什么情况下,一个像素会有多个采样点呢?当我们使用多采样 Anti-aliasing时。为了保持边缘平滑,多采样在像素内的不同位置采样几个点,并将采样值平均以获得最终的像素颜色,这一步通常被称为解决多采样帧缓冲区

XBImageFilters使用GLKProgram类来存储着色器信息。这些程序在XBFilteredView(及其子类)的programs NSArray属性中可用。setValue:forUniformNamed:方法允许您更改着色器中uniform的值。您需要知道其名称(着色器中的变量名称),并提供一个与uniform类型大小相同的缓冲区中的值(数据会被复制,内存由内部管理)。

最简单的片段着色器是输出输入图像的原始颜色

precision mediump float;

uniform sampler2D s_texture;

varying vec2 v_texCoord;

void main()
{
    gl_FragColor = texture2D(s_texture, v_texCoord);
}

precision mediump float语句是必需的,它指定了浮点值的精度。

uniform sampler2D s_texture用于从特定位置(纹理坐标)采样纹理中的颜色。它可以是XBFilteredImageView中的图像或XBFilteredCameraView中的相机图像。您的主要纹理采样器必须命名为s_texture

varying vec2 v_texCoord是当前片段的纹理坐标。它在被渲染/光栅化的三角形中被自动插值。

void main()开始定义将在GPU上为每个片段运行的main函数。我们在这里执行所有必要的疯狂数学。最后,我们必须为gl_FragColor变量分配一个值,它决定了当前片段的最终颜色。在这种情况下,我们只是为v_texCoord纹理坐标赋值的主纹理颜色,这将原图直接映射到屏幕/视图中。texture2D函数执行纹理采样。它接受第一个参数作为采样器,该参数决定了如何采样纹理(缩小过滤器、放大过滤器、包装模式等),第二个参数是归一化空间中的纹理坐标(它在两个维度上从0到1,水平方向上,0是左侧,1是右侧,垂直方向上,0是底部,1是顶部)。

对于任何其他过滤器,原则基本相同。只需计算出将您的颜色进行转换所需的数学方程式并将其编写出来即可。当然,还有一些技巧值得学习,例如查找表、多纹理等。了解一些GLSL内置函数也很重要,如mixsmoothclamp等。查阅GLSL ES规范以获取更多信息,当然也要查看示例过滤器。

内部结构

XBImageFilters使用OpenGL绘制纹理平面,将其上应用任何自定义OpenGL ES 2片元着色器,这为我们提供了很大的自由度,因为我们必须编写算法来计算每个像素的最终颜色。这个过程简单地创建一个矩形(由2个三角形组成),并将图像或数据作为纹理应用于矩形。然后,它设置一些您提供的自定义变换在此矩形上,设置自定义的片元着色器并将结果绘制到屏幕上。当您需要时,可以更改某些参数/统一变量(如模糊滤镜中的模糊半径)并重新绘制。

XBImageFilters的核心位于XBFilteredView中,这是UIView的一个子类,其中封装了其中的所有OpenGL内容。它创建GLKView(是的,本项目使用了GLKit),设置纹理、额外的帧缓冲区和纹理以进行多通道过滤,执行一些渲染到纹理(RTT)步骤,单个简单的顶点缓冲区,内容变换,着色器,绘制代码等。此类不应直接使用。概念上可以考虑它是一个抽象类。如果您认为其他内容可以过滤,可以实现XBFilteredView的子类并通过其受保护方法 _setTextureData:width:height:_updateTextureWithData:提供内容。目前具体的过滤器类只有XBFilteredImageView和XBFilteredCameraView。

XBFilteredImageView是XBImageFilters的子类,它有一个image属性。您可以给它设置一个图像,然后使用方法setFilterFragmentShaderFromFile:error:或使用setFilterFragmentShadersFromFiles:error:设置多个过滤器,然后视图将自动重新绘制。

XBFilteredCameraView是XBImageFilters的子类,允许您实时过滤摄像头的输入图像。它使用AVFoundation捕获摄像头图像。您可以实例化它,设置一些过滤器,并调用startCapturing以开始实时渲染。您也可以使用方法takeAPhotoWithCompletion:拍照为UIImage。

许可协议

本项目受MIT许可协议(详情见COPYRIGHT.txt)管辖。您可以随心所欲地使用它,并且任何形式的贡献都受到热烈欢迎。