【问题标题】:Mapping a texture from "1D" to "2D" with OpenGL matrix transformations使用 OpenGL 矩阵变换将纹理从“1D”映射到“2D”
【发布时间】:2015-05-21 01:02:15
【问题描述】:

(带着这个问题,我正在尝试调查我解决this other one的想法)

如果我在内存中有一个尺寸为 widthheight 的标准二维数组,我可以将其转换为长度为 width 的一维数组* height 然后通过 index = x + y * width 对其进行索引。这种映射在为数组分配和释放内存时非常有用,因为内存管理器无需担心将结构打包为 2D,而只需要担心每个已分配数组的总长度(如果以 1D 表示)。

我正在尝试查看是否可以使用相同的方法来管理 OpenGL 纹理的图像内存。这个想法(如上面链接的问题中所述)是通过bin-packing 将一大堆所需的纹理组合成一个更大的纹理(即,将它们彼此相邻绘制)到大纹理中。这有助于在渲染期间最大限度地减少昂贵的纹理绑定操作。

假设我的大纹理是 8×8 像素(即总共 64 像素):

8x8 texture:                5x5 image:            4x5 image:

   | 0 1 2 3 4 5 6 7           | 0 1 2 3 4           | 0 1 2 3
---+-----------------       ---+-----------       ---+---------
 0 | . . . . . . . .         0 | A B C D E         0 | a b c d
 1 | . . . . . . . .         1 | F G H I J         1 | e f g h
 2 | . . . . . . . .         2 | K L M N O         2 | i j k l
 3 | . . . . . . . .         3 | P Q R S T         3 | m n o p
 4 | . . . . . . . .         4 | U V W X Y         4 | q r s t
 5 | . . . . . . . .
 6 | . . . . . . . .
 7 | . . . . . . . .

我想在其中存储一个 5×5 图像和一个 4×5 图像(即 25 + 20 = 总共 45 个像素)。从技术上讲,我有大量可用像素,但我无法将这些图像彼此相邻放置到大纹理中,因为这需要一个方向的最小尺寸为 9,而另一个方向的最小尺寸为 5。

如果我可以简单地将我的 8×8 纹理视为 64 个连续像素的内存并将两个图像映射到其中的 1D 内存块中,我可以在纹理中按如下方式排列图像: 8x8 纹理:

   | 0 1 2 3 4 5 6 7
---+-----------------
 0 | A B C D E F G H
 1 | I J K L M N O P             
 2 | Q R S T U V W X
 3 | Y a b c d e f g             
 4 | h i j k l m n o             
 5 | p q r s t . . .
 6 | . . . . . . . .
 7 | . . . . . . . .

如果我以 1:1 的比例绘制所有图像,即任何地方都没有小数像素坐标,也不需要任何线性过滤或其他像素混合,是否有可能提出一个我可以使用的转换矩阵使用此纹理绘制 4×5 图像?

使用顶点和片段着色器,这看起来可能相当容易(除非我忘记了什么;我还没有尝试过):

  • 顶点着色器将要绘制的图像的四个角映射到以 64×1 图像表示的纹理:

    • a: (0, 0) → (0 + 0*4 + 25, 0) = (25, 0)     其中 25 是 4×5 图像的偏移量
    • d: (3, 0) → (3 + 0*4 + 25, 0) = (28, 0)
    • q: (0, 4) → (0 + 4*4 + 25, 0) = (41, 0)
    • t: (3, 4) → (3 + 4*4 + 25, 0) = (44, 0)

    纹理内其他坐标的插值应该(?)然后也映射到沿这条线的右侧偏移以获取整数坐标

  • 片段着色器将这个 64×1 坐标转换为最终的 8×8 坐标,方法是简单地取除以 8 的商和余数,例如:
    • a: (0, 25) → (25 % 8, 25 / 8) = (1, 3)
    • d: (0, 28) → (28 % 8, 28 / 8) = (4, 3)
    • k: (0, 35) → (35 % 8, 35 / 8) = (3, 4)
    • q: (0, 41) → (41 % 8, 41 / 8) = (1, 5)
    • t: (0, 44) → (44 % 8, 44 / 8) = (4, 5)

不幸的是,自定义着色器需要 OpenGL ES v2.0 或更高版本,这并非在所有设备上都可用。

是否有可能仅通过 OpenGL ES 1.1 提供的矩阵转换来实现这种映射?

【问题讨论】:

  • 请注意,至少不支持 ES 2.0 的活动设备的百分比已经下降到谷歌停止跟踪它 (developer.android.com/about/dashboards)。我相信他们上次发布 ES 1.1 的数字时大约是 1-2%。考虑到 ES 1.1 已经过时,我实际上会更关心新设备上的 ES 1.1 实现是否仍然有效。
  • 据我所知,没有,因为那不是线性变换,因此不能用矩阵表示。
  • @RetoKoradi 好点。我绝对应该检查性能影响。一般来说,我会尝试支持尽可能多的设备,除非它会导致更现代的设备出现问题。不过,如果标准矩阵变换在现代设备上变得更慢,我会感到惊讶。这仍然是做事的标准方式,不是吗?
  • @chbaker0 我不太确定你在哪里看到了非线性。你的意思是我放在片段着色器部分的模运算吗?也许可以通过打开纹理包装 (GL_REPEAT) 来实现?
  • index = x + y * height 应该是 index = x + y * width

标签: android opengl-es matrix-transform


【解决方案1】:

我还没有尝试过,但我想把它作为一个想法扔出去:

更新:我现在试了一下,只需稍作改动就可以很好地工作(见评论)!

假设我的大纹理的宽度为size,而我要绘制的图像的宽度为width,并从大纹理内的偏移量offset 开始,其中offset 是偏移量的一维表示,即x + y * size

那么,下面的 4x4 矩阵几乎可以实现这个映射:

     _                                           _
    |      1        width        offset      0    |
    |                                             |
    |   1/size   width/size   offset/size    0    |
M = |                                             |
    |      0          0            0         0    |
    |                                             |
    |_     0          0            0         1   _|

因此,在上面的示例中,要绘制 4×5 图像,矩阵将是

 _                    _
|   1    4    25    0  |
|  1/8  1/2  25/8   0  |
|   0    0     0    0  |
|_  0    0     0    1 _|

然后需要使用包含

的 4 向量指定图像坐标
( x, y, 1, 1 )

因此,例如k 的坐标(即 (2,2))将映射到:

M*( 2, 2, 1, 1 ) => ( 35, 4.375, 0, 1 )

将被解释为纹理坐标 (35, 4.375)。

如果我们现在打开最近邻作为插值规则并在 x 方向启用纹理环绕,这应该对应于:

( 3, 4 )

(我在这里使用整数坐标,而在最终实现中,最终坐标需要在 0 到 1 的范围内浮动。这可能很容易通过替换右下角的 1 来实现size 的矩阵,因为这将在输出向量的第四个位置结束,从而除以其他三个。正如@chbaker0 指出的那样,这只有在纹理坐标受通常的透视除法。如果不是,则需要将整个矩阵M除以size才能达到预期的结果。)

这听起来是否合理,或者在我继续尝试实施之前,有人会发现这个问题吗? (可能需要几天时间,因为我必须先做几件事才能获得可测试的应用程序...)

【讨论】:

  • "这实际上可以很容易地通过将矩阵右下角的 1 替换为 size 来实现,因为这将在输出向量的第四个位置结束,从而除以其他三个。”我不确定纹理坐标是否会受到透视分割。规范也不太清楚:khronos.org/registry/gles/specs/1.1/es_full_spec_1.1.12.pdf 第 2.10 节。我通读了它,从我的阅读中我了解到它不受透视划分。
  • @chbaker0 太棒了!我一直很难找到一本好的参考手册。这似乎是一个伟大的!感谢您的链接!如果纹理坐标不受透视除法的影响,只需将整个矩阵除以size,而不是替换右下角的1。所以这不应该是一个交易破坏者......但我会在答案中记下它。
  • 这是最好的,它是 Khronos 发布的官方 OpenGL ES 1.1 规范 :) 我明天会尝试在纸上解决一些问题,因为虽然我的直觉说它不能用矩阵来完成,你的观点很好。
  • @chbaker0 :) 那太棒了,谢谢!如果这可行,我认为这可能对动态纹理生成应用程序的性能大有裨益。
  • @chbaker0 刚刚试了一下:效果很好! :) 只需要进行一项更改:OpenGL 似乎将 0.5 添加到纹理坐标的 y 坐标上,因此要在上面的示例中获得 k,需要指定 (2, 1.5)。或者,可以调整矩阵来解决这个问题。
猜你喜欢
  • 2011-08-25
  • 2014-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
  • 2019-10-20
相关资源
最近更新 更多