如何在GLSL中编写片段着色器以对9个浮点数的数组进行排序

时间:2013-10-18 02:02:45

标签: opengl-es glsl gpgpu

我正在编写一个片段着色器,以便将9张图像放在一起。

我之前从未使用过GLSL,但它似乎是适合这项工作的工具,因为OpenCL在iOS上不可用,并且CPU上的中位数效率低下。 这是我到目前为止所做的:

uniform sampler2D frames[9];
uniform vec2 wh;

void main(void)
{
    vec4 sortedFrameValues[9];
    float sortedGrayScaleValues[9];

    for (int i = 0; i < 9; i++)
    {
        sortedFrameValues[i] = texture2D(frames[i], -gl_FragCoord.xy / wh);
        sortedGrayScaleValues[i] = dot(sortedFrameValues[i].xyz, vec3(0.299, 0.587, 0.114));
    }

        // TODO: Sort sortedGrayScaleValues

    float gray = sortedGrayScaleValues[4];
    gl_FragColor = vec4(gray, gray, gray, 0);
}

4 个答案:

答案 0 :(得分:3)

有点晚了,但我发现的最快方式是插入排序。降低着色器的复杂性和分歧是关键。对于小数字,Bitonic和bubble也很好用。一旦你起步大约100,切换到合并排序。

由于你知道要排序的事物的数量(9),你最好的选择是排序网络。您可以使用this handy tool生成它...

There are 27 comparators in this network,
grouped into 11 parallel operations.

[[0,1],[2,3],[4,5],[7,8]]
[[0,2],[1,3],[6,8]]
[[1,2],[6,7],[5,8]]
[[4,7],[3,8]]
[[4,6],[5,7]]
[[5,6],[2,7]]
[[0,5],[1,6],[3,7]]
[[0,4],[1,5],[3,6]]
[[1,4],[2,5]]
[[2,4],[3,5]]
[[3,4]]

使用它的一种方便方法是声明一个比较和交换宏......

#define CMP(a, b) ...
#define SWAP(a, b) ...
#define CSWAP(a, b) if (CMP(a, b)) {SWAP(a, b);}

CSWAP(0, 1); CSWAP(2, 3); ...

结合这两种方法,一个快速排序小块数据的排序网络,如果你有很多块,则合并排序效果非常好,如Fast Sorting for Exact OIT of Complex Scenes中所述(免责声明:我是作者)。 展开循环(基本上创建排序网络)可能特别有益,允许在寄存器中进行排序。动态索引的数组放在本地内存中,速度很慢。要强制编译器不要这样做,您可以手动声明vec4 array0, array1 ...。宏可以连接这里有用的文本#define CMP(a, b) (array##a < array##b)。一个相当丑陋但又快速的例子是here

答案 1 :(得分:1)

好吧,我最终实现了冒泡排序并使用中间值。

这就是我的解决方案:

uniform sampler2D frames[9];
uniform vec2 wh;

vec4 frameValues[9];
float arr[9];

void bubbleSort()
{
    bool swapped = true;
    int j = 0;
    float tmp;
    for (int c = 0; c < 3; c--)
    {
        if (!swapped)
            break;
        swapped = false;
        j++;
        for (int i = 0; i < 3; i++)
        {
            if (i >= 3 - j)
                break;
            if (arr[i] > arr[i + 1])
            {
                tmp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = tmp;
                swapped = true;
            }
        }
    }
}

void main(void)
{

    for (int i = 0; i < 9; i++)
    {
        frameValues[i] = texture2D(frames[i], -gl_FragCoord.xy / wh);
        arr[i] = dot(frameValues[i].xyz, vec3(0.299, 0.587, 0.114));
    }

    bubbleSort();

    float gray = arr[4];
    gl_FragColor =vec4(gray, gray, gray, 0);
}

答案 2 :(得分:0)

  • 这只是一个正常的排序问题,不是吗?我知道找到中位数的最快方法是Median of Medians方法。

  • 在将值排序之前,不要将您的值放入“已排序”数组中可能更有意义。

  • 您不需要将sortedFrameValues变量作为数组,至少在此处使用它时 - 您再也不会使用任何存储的值。您只需将其作为单个变量。

答案 3 :(得分:0)

您可以在iOS应用中使用OpenGL ES来查找您选择的源像素邻域半径中的中值像素值;它看起来像这样:

kernel vec4 medianUnsharpKernel(sampler u) {
vec4 pixel = unpremultiply(sample(u, samplerCoord(u)));
vec2 xy = destCoord();
int radius = 3;
int bounds = (radius - 1) / 2;
vec4 sum  = vec4(0.0);
for (int i = (0 - bounds); i <= bounds; i++)
{
    for (int j = (0 - bounds); j <= bounds; j++ )
    {
        sum += unpremultiply(sample(u, samplerTransform(u, vec2(xy + vec2(i, j)))));
    }
}
vec4 mean = vec4(sum / vec4(pow(float(radius), 2.0)));
float mean_avg = float(mean);
float comp_avg = 0.0;
vec4 comp  = vec4(0.0);
vec4 median  = mean;
for (int i = (0 - bounds); i <= bounds; i++)
{
    for (int j = (0 - bounds); j <= bounds; j++ )
    {
        comp = unpremultiply(sample(u, samplerTransform(u, vec2(xy + vec2(i, j)))));
        comp_avg = float(comp);
        median = (comp_avg < mean_avg) ? max(median, comp) : median;
    }
}

return premultiply(vec4(vec3(abs(pixel.rgb - median.rgb)), 1.0)); 
}

远没那么复杂,不需要分类。它只涉及两个步骤: 1.计算3x3邻域中源像素周围的像素值的平均值; 2.找到同一邻域中小于均值的所有像素的最大像素值。 3. [可选]从源像素值中减去中值像素值以进行边缘检测。

如果您使用中值进行边缘检测,有几种方法可以修改上述代码以获得更好的结果,即混合中值滤波和截断媒体滤波(替代品和更好的&#39) ;模式&#39;过滤)。如果您有兴趣,请询问。