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

时间:2022-06-02 05:14:01

(With this question I'm trying to investigate an idea I had for solving this other one)

(有了这个问题,我正试图调查一下我解决这个问题的想法)

If I have a standard 2D array of dimensions width and height in memory, I can turn that into a 1D array of length width * height and then index it via index = x + y * width. This mapping is extremely helpful when allocating and freeing memory for the array as the memory manager does not need to worry about packing the structures in 2D but only needs to worry about the overall length of every allocated array if expressed in 1D.

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

I am trying to see if I can use this same approach for image-memory management for OpenGL textures. The idea (as described in the above linked question) is to combine a whole bunch of needed textures into a single bigger one by bin-packing them (i.e. drawing them next to each other) into the big texture. This helps minimize costly texture-binding operations during rendering.

我试图看看我是否可以使用相同的方法来管理OpenGL纹理的图像内存。这个想法(如上面链接的问题中所描述的)是将一大堆所需的纹理组合成一个更大的一个,通过将它们装箱(即将它们彼此相邻)装入大纹理中。这有助于在渲染期间最小化昂贵的纹理绑定操作。

Let's say my big texture is 8×8 pixels (i.e. 64 pixels total):

假设我的大纹理是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 | . . . . . . . .

And I would like to store a 5×5 image and a 4×5 image in it (i.e. 25 + 20 = 45 pixels total). Technically, I have plenty of pixels available, but I can't place these images next to each other into the big texture as that would require a minimum dimension of 9 in one direction and 5 in the other.

并且我想在其中存储5×5图像和4×5图像(即,总共25 + 20 = 45个像素)。从技术上讲,我有足够的像素可供选择,但我不能将这些图像彼此相邻放入大纹理中,因为这需要在一个方向上最小尺寸为9,在另一个方向上最小尺寸为5。

If I could simply treat my 8×8 texture as 64 continues pixels of memory and map the two images into 1D blocks of memory inside that, I could arrange the images as follows inside the texture: 8x8 texture:

如果我可以简单地将我的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 | . . . . . . . .

If I draw all my images at a scale of 1:1, i.e. no fractional pixel coordinates anywhere and no need for any linear filtering or other pixel blending, is it possible to come up with a transformation matrix that I can use to draw the 4×5 image using this texture?

如果我以1:1的比例绘制我的所有图像,即没有任何分数像素坐标,也不需要任何线性滤波或其他像素混合,是否可以提出一个可用于绘制4的变换矩阵×5图像使用这种纹理?

With vertex and fragment shaders, this looks like it might be fairly easy (unless I'm forgetting something; I haven't tried this):

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

  • The vertex shader maps the four corners of the image to draw to the texture expressed as a 64×1 image:

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

    • a: (0, 0) → (0 + 0*4 + 25, 0) = (25, 0)     where 25 is the offset of the 4×5 image
    • a:(0,0)→(0 + 0 * 4 + 25,0)=(25,0)其中25是4×5图像的偏移

    • d: (3, 0) → (3 + 0*4 + 25, 0) = (28, 0)
    • d:(3,0)→(3 + 0 * 4 + 25,0)=(28,0)

    • q: (0, 4) → (0 + 4*4 + 25, 0) = (41, 0)
    • q:(0,4)→(0 + 4 * 4 + 25,0)=(41,0)

    • t: (3, 4) → (3 + 4*4 + 25, 0) = (44, 0)
    • t:(3,4)→(3 + 4 * 4 + 25,0)=(44,0)

    The interpolation of other coordinates inside the texture should (?) then also map to the right offset along this line for integer coordinates

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

  • The fragment shader converts this 64×1-coordinate into the final 8×8 coordinate by simply taking the quotient and remainder of a division by 8, e.g.:
    • a: (0, 25) → (25 % 8, 25 / 8) = (1, 3)
    • a:(0,25)→(25%8,25 / 8)=(1,3)

    • d: (0, 28) → (28 % 8, 28 / 8) = (4, 3)
    • d:(0,28)→(28%8,28 / 8)=(4,3)

    • k: (0, 35) → (35 % 8, 35 / 8) = (3, 4)
    • k:(0,35)→(35%8,35 / 8)=(3,4)

    • q: (0, 41) → (41 % 8, 41 / 8) = (1, 5)
    • 问:(0,41)→(41%8,41 / 8)=(1,5)

    • t: (0, 44) → (44 % 8, 44 / 8) = (4, 5)
    • t:(0,44)→(44%8,44 / 8)=(4,5)

  • 片段着色器通过简单地将除数的除数和余数除以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)

Unfortunately custom shaders require OpenGL ES v2.0 or better, which is not available on all devices.

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

Is it somehow possible to achieve this mapping just via the matrix transformations offered by OpenGL ES 1.1?

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

1 个解决方案

#1


0  

I haven't tried this yet, but I wanted to throw it out there as an idea already:

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

UPDATE: I tried it now and it works beautifully with one minor change (see comment)!

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

Let's say my big texture has width size and the image I want to draw has width width and starts at offset offset inside the big texture, where offset is the 1-D representation of the offset, i.e. x + y * size.

假设我的大纹理具有宽度大小,并且我想要绘制的图像具有宽度宽度并且从大纹理内的偏移偏移开始,其中偏移是偏移的1-D表示,即x + y *大小。

Then, the following 4x4 matrix will almost achieve this mapping:

然后,以下4x4矩阵几乎将实现此映射:

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

So, in the example above, to draw the 4×5 image, the matrix would be

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

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

The image coordinates will then need to be specified with a 4-vector containing

然后需要使用包含4向量的图像坐标进行指定

( x, y, 1, 1 )

So, for example the coordinates of k (i.e. (2,2)) will map to:

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

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

which will be interpreted as texture coordinate (35, 4.375).

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

If we now turn on nearest neighbor as the interpolation rule and enable texture wrapping in the x-direction, this should correspond to:

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

( 3, 4 )

(I was using integer coordinates here, whereas in the final implementation, the final coordinates would need to be floats in the range from 0 to 1. This might be achievable very easily by replacing the 1 in the bottom right corner of the matrix with size, since that will end up in the fourth position of the output vector and thus divide the other three. This, as @chbaker0 pointed out, would only work, though, if the texture coordinates are subject to the usual perspective division. If they are not, the entire matrix M needs to be divided by size instead to achieve the desired result.)

(我在这里使用整数坐标,而在最后的实现中,最终坐标需要在0到1的范围内浮动。这可以通过用矩阵替换矩阵右下角的1来轻松实现。因为那将最终位于输出矢量的第四个位置,因此除了其他三个。这个,正如@ chbaker0指出的那样,但是,如果纹理坐标受到通常的透视分割的影响,那么它只会起作用。如果它们是不是,整个矩阵M需要按大小划分,而不是达到预期的效果。)

Does this sound reasonable at all or can someone see a problem with this before I go ahead and try to implement this? (Might take me a few days, since I have to do a couple other things first to get to a testable app...)

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

#1


0  

I haven't tried this yet, but I wanted to throw it out there as an idea already:

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

UPDATE: I tried it now and it works beautifully with one minor change (see comment)!

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

Let's say my big texture has width size and the image I want to draw has width width and starts at offset offset inside the big texture, where offset is the 1-D representation of the offset, i.e. x + y * size.

假设我的大纹理具有宽度大小,并且我想要绘制的图像具有宽度宽度并且从大纹理内的偏移偏移开始,其中偏移是偏移的1-D表示,即x + y *大小。

Then, the following 4x4 matrix will almost achieve this mapping:

然后,以下4x4矩阵几乎将实现此映射:

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

So, in the example above, to draw the 4×5 image, the matrix would be

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

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

The image coordinates will then need to be specified with a 4-vector containing

然后需要使用包含4向量的图像坐标进行指定

( x, y, 1, 1 )

So, for example the coordinates of k (i.e. (2,2)) will map to:

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

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

which will be interpreted as texture coordinate (35, 4.375).

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

If we now turn on nearest neighbor as the interpolation rule and enable texture wrapping in the x-direction, this should correspond to:

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

( 3, 4 )

(I was using integer coordinates here, whereas in the final implementation, the final coordinates would need to be floats in the range from 0 to 1. This might be achievable very easily by replacing the 1 in the bottom right corner of the matrix with size, since that will end up in the fourth position of the output vector and thus divide the other three. This, as @chbaker0 pointed out, would only work, though, if the texture coordinates are subject to the usual perspective division. If they are not, the entire matrix M needs to be divided by size instead to achieve the desired result.)

(我在这里使用整数坐标,而在最后的实现中,最终坐标需要在0到1的范围内浮动。这可以通过用矩阵替换矩阵右下角的1来轻松实现。因为那将最终位于输出矢量的第四个位置,因此除了其他三个。这个,正如@ chbaker0指出的那样,但是,如果纹理坐标受到通常的透视分割的影响,那么它只会起作用。如果它们是不是,整个矩阵M需要按大小划分,而不是达到预期的效果。)

Does this sound reasonable at all or can someone see a problem with this before I go ahead and try to implement this? (Might take me a few days, since I have to do a couple other things first to get to a testable app...)

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