注意:现在我正在模拟器中对此进行测试。但我的想法是,我可以在 iPhone 4s 中获得可接受的性能。 (我知道,我应该在设备上进行测试,但我要过几天才能拿到设备)。
我正在尝试制作一个卷积着色器,该着色器允许使用支持 3x3、5x5 或 7x7 的滤镜和多 channel 选项对图像进行卷积。我猜着色器本身可以工作。但我注意到以下几点:
- 一个简单的 3x3 盒式过滤器,单次通过,几乎不会使图像模糊。因此,为了获得更明显的模糊效果,我必须使用 3x3 2-pass 或 5x5。
- 最简单的情况(3x3,1 次通过)已经足够慢,无法以 30 fps 的速度使用。
到目前为止,我尝试了两种方法(这是针对我为 iPhone 做的一些基于 OGLES2 的插件,这就是这些方法的原因):
- (NSString *)vertexShader
{
return SHADER_STRING
(
attribute vec4 aPosition;
attribute vec2 aTextureCoordinates0;
varying vec2 vTextureCoordinates0;
void main(void)
{
vTextureCoordinates0 = aTextureCoordinates0;
gl_Position = aPosition;
}
);
}
- (NSString *)fragmentShader
{
return SHADER_STRING
(
precision highp float;
uniform sampler2D uTextureUnit0;
uniform float uKernel[49];
uniform int uKernelSize;
uniform vec2 uTextureUnit0Offset[49];
uniform vec2 uTextureUnit0Step;
varying vec2 vTextureCoordinates0;
void main(void)
{
vec4 outputFragment = texture2D(uTextureUnit0, vTextureCoordinates0 + uTextureUnit0Offset[0] * uTextureUnit0Step) * uKernel[0];
for (int i = 0; i < uKernelSize; i++) {
outputFragment += texture2D(uTextureUnit0, vTextureCoordinates0 + uTextureUnit0Offset[i] * uTextureUnit0Step) * uKernel[i];
}
gl_FragColor = outputFragment;
}
);
}
这种方法的想法是,过滤器值和获取纹素的偏移坐标都在客户端/应用程序领域预先计算一次,然后在制服中设置。然后,着色器程序将在任何时候使用它们。请注意,统一数组 (49) 的大尺寸是因为我可能会做一个 7x7 内核。
这种方法每次通过需要 0.46 秒。
然后我尝试了以下方法:
- (NSString *)vertexShader
{
return SHADER_STRING
(
// Default pass-thru vertex shader:
attribute vec4 aPosition;
attribute vec2 aTextureCoordinates0;
varying highp vec2 vTextureCoordinates0;
void main(void)
{
vTextureCoordinates0 = aTextureCoordinates0;
gl_Position = aPosition;
}
);
}
- (NSString *)fragmentShader
{
return SHADER_STRING
(
precision highp float;
uniform sampler2D uTextureUnit0;
uniform vec2 uTextureUnit0Step;
uniform float uKernel[49];
uniform float uKernelRadius;
varying vec2 vTextureCoordinates0;
void main(void)
{
vec4 outputFragment = vec4(0., 0., 0., 0.);
int kRadius = int(uKernelRadius);
int kSupport = 2 * kRadius + 1;
for (int t = -kRadius; t <= kRadius; t++) {
for (int s = -kRadius; s <= kRadius; s++) {
int kernelIndex = (s + kRadius) + ((t + kRadius) * kSupport);
outputFragment += texture2D(uTextureUnit0, vTextureCoordinates0 + (vec2(s,t) * uTextureUnit0Step)) * uKernel[kernelIndex];
}
}
gl_FragColor = outputFragment;
}
);
}
这里,我还是通过uniform把预计算好的kernel传给了fragment shader。但是我现在在着色器中计算纹素偏移量甚至内核索引。我预计这种方法会更慢,因为我不仅有 2 个 for 循环,而且我还为每个片段做了一堆额外的计算。
有趣的是,这种方法只需要 0.42 秒。实际上更快...
在这一点上,我唯一能想到的另一件事是通过将 2D 内核视为两个可分离的 1D 内核来将卷积分为 2 次传递。还没试过。
只是为了比较,请注意以下示例是框过滤的具体实现,A - 几乎是硬编码的,B - 并不真正遵守经典 nxn 线性滤波器的理论定义(它不是矩阵加起来不等于 1),我尝试了 OpenGL ES 2.0 编程指南中的这种方法:
- (NSString *)fragmentShader
{
return SHADER_STRING
(
// Default pass-thru fragment shader:
precision mediump float;
// Input texture:
uniform sampler2D uTextureUnit0;
// Texel step:
uniform vec2 uTextureUnit0Step;
varying vec2 vTextureCoordinates0;
void main() {
vec4 sample0;
vec4 sample1;
vec4 sample2;
vec4 sample3;
float step = uTextureUnit0Step.x;
sample0 = texture2D(uTextureUnit0, vec2(vTextureCoordinates0.x - step, vTextureCoordinates0.y - step));
sample1 = texture2D(uTextureUnit0, vec2(vTextureCoordinates0.x + step, vTextureCoordinates0.y + step));
sample2 = texture2D(uTextureUnit0, vec2(vTextureCoordinates0.x + step, vTextureCoordinates0.y - step));
sample3 = texture2D(uTextureUnit0, vec2(vTextureCoordinates0.x - step, vTextureCoordinates0.y + step));
gl_FragColor = (sample0 + sample1 + sample2 + sample3) / 4.0;
}
);
}
这种方法每次通过需要 0.06 秒。 请注意,以上是我的改编,其中我使步骤与我在实现中使用的纹素偏移几乎相同。通过这一步,结果与我的实现非常相似,但 OpenGL 指南中的原始着色器使用了更大的步长,模糊了更多。
综上所述,我的问题实际上有两个方面:
- 我正在计算步长/纹素偏移量作为 vec2(1/图像宽度,1/图像高度)。有了这个偏移量,就像我说的,一个 3x3 盒式滤波器几乎不引人注意。这个对吗?还是我误解了步骤的计算或其他什么?
- 还有什么我可以做的来尝试让“一般情况下的卷积”方法足够快地实时运行吗?或者我是否需要像 OpenGL 示例那样进行简化?
最佳答案
如果您通过 Instruments 中的 OpenGL ES 分析工具或 Xcode 中的帧调试器运行它们,您可能会看到关于 dependent texture reads 的注释-- 你在片段着色器中计算纹理坐标,这意味着硬件在评估着色器之前无法获取纹理数据。如果进入片段着色器的纹素坐标已知,则硬件可以与其他任务并行预取纹素数据,因此在片段着色器需要它时它已准备就绪。
您可以通过在顶点着色器中预先计算纹素坐标来大大加快速度。布拉德·拉森 (Brad Larson) 在 this answer 中有一个很好的例子类似的问题。
关于ios - OpenGL ES 2 中足够快的实时卷积着色器的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18939890/