使用OpenGL着色器的高斯滤波器

nac*_*o4d 11 iphone shader opengl-es

我正在尝试学习Shaders在我的iPhone应用程序中实现一些东西.到目前为止,我已经理解了简单的例子,例如将彩色图像制作成灰度,阈值等.大多数示例涉及简单的操作,其中处理输入图像像素I(x,y)导致对同一像素的颜色的简单修改

但是,Convolutions怎么样?例如,最简单的例子是高斯滤波器,

其中输出图像像素O(x,y)不仅取决于I(x,y)周围的8个像素,还取决于周围的8个像素

O(x,y) = (I(x,y)+ surrounding 8 pixels values)/9;
Run Code Online (Sandbox Code Playgroud)

通常,使用单个图像缓冲区无法完成此操作,或者输入像素将随执行滤镜而更改.如何使用着色器执行此操作?另外,我应该自己处理边框吗?或者有一个内置函数或检查无效像素访问的东西,如 I(-1,-1)

提前致谢

PS:我会很慷慨(阅读:给出很多观点);)

Bra*_*son 10

DanielRákos提出了一种高度优化的基于着色器的方法,用于执行九次高斯模糊处理.他的过程使用硬件中纹理过滤提供的底层插值来执行九次击中滤波器,每次传递仅使用五次纹理读取.这也被分成单独的水平和垂直通道,以进一步减少所需的纹理读取次数.

我将针对OpenGL ES和iOS GPU的实现调整到我的图像处理框架(在GPUImageFastBlurFilter类下).在我的测试中,它可以在iPhone 4上以2.0毫秒执行640x480帧的单次模糊传递,这非常快.

我使用了以下顶点着色器:

 attribute vec4 position;
 attribute vec2 inputTextureCoordinate;

 uniform mediump float texelWidthOffset; 
 uniform mediump float texelHeightOffset; 

 varying mediump vec2 centerTextureCoordinate;
 varying mediump vec2 oneStepLeftTextureCoordinate;
 varying mediump vec2 twoStepsLeftTextureCoordinate;
 varying mediump vec2 oneStepRightTextureCoordinate;
 varying mediump vec2 twoStepsRightTextureCoordinate;

 void main()
 {
     gl_Position = position;

     vec2 firstOffset = vec2(1.3846153846 * texelWidthOffset, 1.3846153846 * texelHeightOffset);
     vec2 secondOffset = vec2(3.2307692308 * texelWidthOffset, 3.2307692308 * texelHeightOffset);

     centerTextureCoordinate = inputTextureCoordinate;
     oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset;
     twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset;
     oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset;
     twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset;
 }
Run Code Online (Sandbox Code Playgroud)

和以下片段着色器:

 precision highp float;

 uniform sampler2D inputImageTexture;

 varying mediump vec2 centerTextureCoordinate;
 varying mediump vec2 oneStepLeftTextureCoordinate;
 varying mediump vec2 twoStepsLeftTextureCoordinate;
 varying mediump vec2 oneStepRightTextureCoordinate;
 varying mediump vec2 twoStepsRightTextureCoordinate;

// const float weight[3] = float[]( 0.2270270270, 0.3162162162, 0.0702702703 );

 void main()
 {
     lowp vec3 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate).rgb * 0.2270270270;
     fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate).rgb * 0.3162162162;
     fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate).rgb * 0.3162162162;
     fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate).rgb * 0.0702702703;
     fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate).rgb * 0.0702702703;

     gl_FragColor = vec4(fragmentColor, 1.0);
 }
Run Code Online (Sandbox Code Playgroud)

执行此操作.两次传递可以通过为texelWidthOffset(垂直传递)发送0值,然后将该结果输入到为texelHeightOffset(为水平传递)给出0值的运行来实现.

我还在上面链接的框架中有一些更高级的卷积示例,包括Sobel边缘检测.

  • @GingerBreadMane - 对于iOS设备中的PowerVR基于图块的延迟渲染器(这里是问题的主题),在顶点着色器中计算它可以避免依赖纹理读取.由于缓存纹理提取的方式,这为这些设备提供了巨大的性能提升. (2认同)