【问题标题】:How to load OpenGL texture from ARGB NSImage without swizzling?如何从 ARGB NSImage 加载 OpenGL 纹理而不进行调整?
【发布时间】:2011-02-10 14:58:34
【问题描述】:

我正在为 Mac OS >= 10.6 编写一个应用程序,该应用程序从磁盘加载的图像创建 OpenGL 纹理。

首先,我将图像加载到 NSImage 中。然后我从图像中获取 NSBitmapImageRep 并使用 glTexImage2D 将像素数据加载到纹理中。

对于 RGB 或 RGBA 图像,它可以完美运行。我可以传入 3 字节/像素的 RGB 或 4 字节的 RGBA,并创建一个 4 字节/像素的 RGBA 纹理。

但是,我刚刚让测试人员向我发送了一张似乎具有 ARGB 字节顺序的 JPEG 图像(在佳能 EOS 50D 上拍摄,不确定它是如何导入的)。

我在这个线程上找到了一个帖子:(http://www.cocoabuilder.com/archive/cocoa/12782-coregraphics-over-opengl.html)这表明我指定了 GL_BGRA 的格式参数 glTexImage2D 和 GL_UNSIGNED_INT_8_8_8_8_REV 类型。

这似乎合乎逻辑,并且似乎应该有效,但事实并非如此。我得到不同但仍然错误的颜色值。

我编写了“swizzling”(手动字节交换)代码,将 ARGB 图像数据洗牌到一个新的 RGBA 缓冲区中,但是这种逐字节的混合对于大图像来说会很慢。

我还想了解如何以“正确的方式”进行这项工作。

将 ARGB 数据加载到 RGBA OpenGL 纹理中的技巧是什么?

我当前对 xxx 的调用如下所示:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newWidth, newHeight, 0, format, GL_UNSIGNED_BYTE, pixelBuffer);

其中是 RGB 或 RGBA。

我尝试使用:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newWidth, newHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer);

当我的图像代表报告它处于“alpha 优先”顺序时。

作为第二个问题,我还了解到大多数显卡的“原生”格式是 GL_BGRA,因此以该格式创建纹理可以加快纹理绘制速度。纹理绘制的速度比加载纹理的速度更重要,因此预先将数据“混合”为 BGRA 格式是值得的。我尝试通过指定 GL_RGBA 的“内部格式”来让 OpenGL 创建 BGRA 纹理,但这会导致图像完全变黑。我对文档的解释使我期望 glTexImage2D 会在读取数据时对数据进行字节交换,如果源格式和内部格式不同,但是当我尝试指定“内部格式”时,我得到一个 OpenGL 错误 0x500 (GL_INVALID_ENUM) GL_RGBA。我错过了什么?

【问题讨论】:

    标签: opengl jpeg loading nsimage argb


    【解决方案1】:

    我不知道将 ARGB 数据直接加载到纹理中的方法,但有比仅在 CPU 上进行调配更好的解决方法。你可以在 GPU 上非常有效地做到这一点:

    1. 将 ARGB 数据加载到临时 RGBA 纹理中。
    2. 使用此纹理绘制一个全屏四边形,同时使用简单的像素着色器渲染到目标纹理中。
    3. 继续加载其他资源,无需停止 GPU 管道。

    像素着色器示例:

    #version 130
    uniform sampler2DRect unit_in;
    void main() {
        gl_FragColor = texture( unit_in, gl_FragCoord.xy ).gbar;
    }
    

    【讨论】:

    • 我的应用程序是使用固定管道 OpenGL 编写的(没有片段着色器)。我现在已经在 iOS 上完成了基于着色器的编程,但还没有在 Mac OS 上解决它。如何将固定流水线 OpenGL 与片段着色器混合在一起,而片段着色器只能用于我的 swizzling?
    • 我已经很久没有使用固定功能 GL,但我记得绑定像素着色器只是覆盖了一些固定功能状态,因此您应该确保这个着色器也可以除了 swizzling 之外,固定功能部分过去的一切。
    • 我是菜鸟@kvark 可以分享一个例子
    【解决方案2】:

    你是用 OpenGL 来渲染它的,对吧? 如果您想以简单的方式做到这一点,您可以让像素着色器实时调整颜色。这对显卡来说完全没有问题,它们是用来做更复杂的事情的:)。

    你可以使用这样的着色器:

    uniform sampler2D image;
    void main()
    {
        gl_FragColor = texture2D(image, gl_FragCoord.xy).gbar;
    }
    

    如果您不了解着色器,请在此处阅读这篇短文:http://www.lighthouse3d.com/opengl/glsl/

    【讨论】:

    • 我看不出您的回答如何扩展我的回答,那么重点是什么?此外,您的代码不正确:gl_FragCoord 是窗口坐标,未标准化,因此您无法使用它对“sampler2D”进行采样:P
    【解决方案3】:

    这个问题很老,但如果其他人正在寻找这个问题,我发现了一个不是严格安全但有效的解决方案。问题是每个 32 位 RGBA 值都将 A 作为第一个字节而不是最后一个字节。

    NBitmapImageRep.bitmapData 给你一个指向第一个字节的指针,你给 OpenGL 作为指向它的像素的指针。只需在该指针上加 1,然后以正确的顺序指向 RGB 值,下一个像素的 A 位于末尾。

    这样做的问题是最后一个像素会从图像末尾的一个字节中获取 A 值,并且 A 值都是一个像素。但就像提问者一样,我在加载 JPG 时得到了这个,所以无论如何 alpha 是无关紧要的。这似乎不会导致问题,但我不会声称它是“安全的”。

    【讨论】:

      【解决方案4】:

      纹理的名称,其数据为 ARGB 格式。

      GLuint argb_texture;
      

      在一个函数调用中设置 ARGB swizzle 的标记数组。

      static const GLenum argb_swizzle[] =
      {
         GL_GREEN, GL_BLUE, GL_ALPHA, GL_RED
      };
      

      绑定ARGB纹理

      glBindTexture(GL_TEXTURE_2D, argb_texture);
      

      一次调用 glTexParameteriv 设置所有四个 swizzle 参数

      glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, argb_swizzle);
      

      我知道这项工作,但我不确定 argb_swizzle 的顺序是否正确。如果这不正确,请纠正我。我不太清楚 argb_swizzle 中的 GL_GREEN、G​​L_BLUE、GL_ALPHA、GL_RED 是如何确定的。

      正如The OpenGL Programming Guide 建议的那样:

      ...这是一种允许您重新排列组件的机制 图形读取时动态纹理数据的顺序 硬件。

      【讨论】:

        猜你喜欢
        • 2017-06-11
        • 1970-01-01
        • 1970-01-01
        • 2021-01-16
        • 1970-01-01
        • 2020-05-08
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        相关资源
        最近更新 更多