使用OpenGL矩阵变换将纹理从“1D”映射到“2D”

时间:2015-05-21 01:02:15

标签: android opengl-es matrix-transform

(有了这个问题,我正在尝试调查我解决的问题this other one

如果我在内存中有一个尺寸为 width height 的标准2D数组,我可以将其转换为长度为 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)

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

  • 片段着色器通过简单地将除数的商和余数除以8,将此64×1坐标转换为最终的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提供的矩阵转换以某种方式实现此映射?

1 个答案:

答案 0 :(得分:0)

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

更新:我现在尝试了它并且只需稍作修改就能很好地发挥作用(见评论)!

假设我的大纹理宽度为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,以达到预期的结果。)

这听起来是否合理,或者有人在我继续尝试实现此问题之前可以看到问题? (可能需要几天时间,因为我必须先做其他一些事情才能找到可测试的应用程序...)