实际上,您似乎要求的东西是不可能的,或者至少不像在 GPU 上看起来那么容易。问题在于 GPU 旨在在最短的时间内完成尽可能多的小任务。不包括遍历像素等数据数组,因此获取整数或浮点值之类的数据可能有点困难。
您可以尝试一种非常有趣的程序,但我不能说结果适合您:
您可以首先创建一个新纹理,它是两个输入纹理之间的差异,然后继续对结果进行下采样直到 1x1 像素纹理,并获取该像素的值以查看它的差异。
要实现这一点,最好使用固定大小的目标缓冲区,即 POT(2 的幂),例如 256x256。如果您没有使用固定尺寸,则结果可能会因图像尺寸而有很大差异。
所以在第一遍中,您会将两个纹理重绘到第三个(使用 FBO - 帧缓冲区对象)。您将使用的着色器很简单:
vec4 a = texture2D(iChannel0,uv);
vec4 b = texture2D(iChannel1,uv);
fragColor = abs(a-b);
所以现在你有了一个纹理,它代表了两个图像每个像素、每个颜色分量之间的差异。如果两张图片相同,则结果将是全黑图片。
现在您需要创建一个新的 FBO,它在每个维度上都缩小一半,在本示例中为 128x128。要绘制到这个缓冲区,您需要使用GL_NEAREST 作为纹理参数,因此不会对纹理提取进行插值。然后对于每个新像素,将源图像的 4 个最近像素相加:
vec4 originalTextCoord = varyingTextCoord;
vec4 textCoordRight = vec2(varyingTextCoord.x+1.0/256, varyingTextCoord.y);
vec4 textCoordBottom = vec2(varyingTextCoord.x, varyingTextCoord.y+1.0/256);
vec4 textCoordBottomRight = vec2(varyingTextCoord.x+1.0/256, varyingTextCoord.y+1.0/256);
fragColor = texture2D(iChannel0, originalTextCoord) +
texture2D(iChannel0, textCoordRight) +
texture2D(iChannel0, textCoordBottom) +
texture2D(iChannel0, textCoordBottomRight);
256 值来自源纹理,因此它应该是统一的,因此您可以重复使用相同的着色器。
绘制完成后,需要下拉到 64、32、16... 然后将像素读回 CPU 并查看结果。
现在不幸的是,此过程可能会产生非常不希望的结果。由于颜色只是简单地加在一起,这将对所有不够相似的图像产生溢出(导致白色像素或(1,1,1,0) 不透明)。这可以通过在第一个着色器通道上使用比例来克服,将输出除以足够大的值。但这可能还不够,可能需要在第二个着色器中进行平均(将所有texture2D 调用乘以.25)。
最终结果可能还是有点奇怪。您在 CPU 上获得 4 个颜色分量,它们代表图像差异的总和或平均值。我想您可以总结它们并选择您认为图像是否相似的内容。但是,如果您想更清楚地了解您得到的结果,您可能希望将整个像素视为单个 32 位浮点值(这些有点棘手,但您可能会在 SO 周围找到答案)。这样,您可以计算没有溢出的值,并从算法中获得非常准确的结果。这意味着您将写入浮动值,就好像它是一种颜色,从第一个着色器输出开始并在每个其他绘制调用中继续(获取 texel,将其转换为浮点数,求和,将其转换回 vec4 并分配为输出) , GL_NEAREST 在这里是必不可少的。
如果没有,那么您可以优化程序并使用GL_LINEAR 而不是GL_NEAREST,并简单地重新绘制差分纹理,直到它达到单个像素大小(不需要 4 个坐标)。这应该会产生一个很好的像素,它代表差分纹理中所有像素的平均值。所以这是两个图像中像素之间的平均差异。这个过程也应该很快。
然后,如果您想做一些更智能的算法,您可能会在创建差分纹理方面创造一些奇迹。简单地减去颜色可能不是最好的方法。模糊其中一张图像然后将其与另一张图像进行比较会更有意义。对于那些非常相似的图像,这将失去精度,但对于其他一切,它会给你一个更好的结果。例如,您可以说只有当像素与另一张图像(模糊的)的权重相差 30% 时您才感兴趣,因此您将丢弃并缩放每个组件的 30%,例如 result.r = clamp(abs(a.r-b.r)-30.0/100.0, .0, 1.0)/((100.0-30.0)/100.0);