0

我正在使用SDL2OpenGL创建上下文。我使用SDL_image加载图像,并将它们绑定到OpenGL纹理。但是因为坐标系不一样,所以纹理被翻转了。

我找到了两种解决方法:

加载后修改纹理

优点:每个纹理只做一次

缺点:使用 CPU 完成,这会减慢每个纹理的加载速度

渲染时在 Y 轴和 Z 轴上应用 180° 旋转

优点:使用超快速功能

缺点:每帧需要做多次

在使用SDL_Image加载纹理后,是否有另一种方法可以翻转纹理?如果不是,通常使用哪种方法?

4

2 回答 2

2

有很多选择。想到的一些:

编辑原始资产

您可以使用图像处理工具将图像文件倒置,并将翻转的图像用作您的资产。在图像查看器中查看时,它们看起来会颠倒,但在用作纹理时会变得正确。

如果您完全控制图像,这是理想的解决方案。如果您在运行时从外部来源获取图像,它显然不起作用。

图像加载期间翻转

一些图像加载库允许您在加载过程中翻转图像。从我能找到的 SOIL_image 的文档中,我在那里没有看到这个选项。但是您也许可以找到支持它的替代库。当然,如果您编写自己的图像加载程序,您也可以这样做。

这是一个很好的解决方案。无论如何,当您在触摸数据时进行翻转,开销是最小的。一种常见的方法是逐行读取数据,并以相反的顺序存储在纹理中,使用glTexSubImage2D().

在加载和首次使用之间切换

您可以在加载后创建纹理的翻转副本。执行此操作的典型方法是绘制屏幕大小的四边形,同时对原始纹理进行采样并渲染到 FBO,该 FBO 将生成的翻转纹理作为渲染目标。或者,更优雅,使用glBlitFramebuffer().

这不是很吸引人,因为它涉及复制内存。虽然让 GPU 创建副本应该非常有效,但额外的复制总是不可取的。即使每个纹理只发生一次,它也会增加您的启动/加载时间。

将变换应用于纹理坐标

您可以在顶点或片段着色器中对纹理坐标应用变换。你在谈论你的问题中的旋转,但你需要的转换实际上是微不足道的。您基本上只是y将纹理坐标映射到1.0 - y,并x保持不变。

这增加了着色器执行的一小部分代价。但与随之而来的纹理采样操作相比,该操作非常简单快捷。实际上,增加的开销可能微不足道。虽然我不认为它很漂亮,但它是一个完美的解决方案。

反转纹理坐标

这类似于前一个选项,但不是在着色器中反转纹理坐标,而是已经在顶点属性数据中指定它们反转。

这通常是微不足道的。例如,使用(0, 0), (1, 0), (0, 1),的纹理坐标(1, 1)对 4 个角进行纹理四边形是很常见的。相反,您只需在纹理坐标的第二个组件中替换0with11with 0

或者说您从文件中加载包含纹理坐标的模型。您只需在读取期间替换y纹理坐标中的每个1.0f - y,然后再存储纹理坐标以供以后渲染。

恕我直言,这通常是最好的解决方案。做起来很简单,而且基本上没有性能损失。

于 2015-06-27T03:25:13.087 回答
2

我不同意之前答案的大部分观点,除了在加载时或在首次使用之前翻转图像。

原因是,如果您遵循数据驱动的软件开发实践,则绝不应该让代码决定数据的性质。软件的设计应该能够准确地支持数据。其他任何事情都不适合目的。

修改纹理坐标是骇人听闻的,尽管它很容易使用。如果您在稍后阶段决定使用不翻转图像的不同图像库会发生什么?现在您的图像将在渲染过程中再次反转。

相反,从源头处理问题,在加载期间或首次使用之前翻转图像(我主张在加载时,因为它可以集成到通过 SDL_Image 加载图像的代码中,因此更容易维护)。

为了翻转图像,我将发布一些简单的伪代码来说明如何做到这一点:

function flip_image( char* bytes, int width, int height, int bytes_per_pixel):

char buffer[bytes_per_pixel*width]

for ( i = 0 -> height/2 ) loop
    offset = bytes + bytes_per_pixel*width * i
    copy row (offset -> offset + bytes_per_pixel*width) -> buffer
    offset2 bytes + bytes_per_pixel * height * width;
    copy row (offset2 -> offset2 + bytes_per_pixel*width) ->  (offset -> offset + bytes_per_pixel*width)
    copy row(buffer -> buffer + width * bytes_per_pixel ) -> offset
end loop

这是此代码循环的一次迭代的可视化说明:

行交换

  1. 将当前行 N 复制到缓冲区
  2. 将行 (rows - N) 复制到行 N
  3. 将缓冲区复制到行(行 - N)
  4. 增加 N 并重复直到 N == rows/2

但是,这仅适用于具有偶数行的图像,这很好,因为 opengl 不喜欢具有非二维幂的纹理。

还应该注意的是,如果加载的图像没有二次宽度的幂,SDL_Image 会填充它。因此,传递给函数的“宽度”应该是图像的间距,而不是宽度。

于 2017-04-13T15:25:57.003 回答