使用 OpenGL 矩阵变换将纹理从“一维”映射到“二维”
Mapping a texture from "1D" to "2D" with OpenGL matrix transformations
(带着这个问题,我正在尝试研究解决 this other one 的想法)
如果我在内存中有一个尺寸为 width 和 height 的标准二维数组,我可以将其转换为长度为的一维数组width * height 然后通过 index = x[=71= 索引它] + y * 宽度。这种映射在为数组分配和释放内存时非常有用,因为内存管理器不需要担心以 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 个连续的内存像素并将两个图像映射到其中的一维内存块,我可以在纹理内按如下方式排列图像:
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 提供的矩阵变换以某种方式实现此映射?
我还没有尝试过这个,但我已经想把它作为一个想法扔出去了:
更新:我现在尝试了它,只需稍作改动就可以很好地工作(见评论)!
假设我的大纹理的宽度为 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
而不是达到预期的结果。)
这听起来是否合理,或者有人可以在我继续尝试实施之前发现这方面的问题吗? (可能需要几天时间,因为我必须先做一些其他事情才能获得可测试的应用程序...)
(带着这个问题,我正在尝试研究解决 this other one 的想法)
如果我在内存中有一个尺寸为 width 和 height 的标准二维数组,我可以将其转换为长度为的一维数组width * height 然后通过 index = x[=71= 索引它] + y * 宽度。这种映射在为数组分配和释放内存时非常有用,因为内存管理器不需要担心以 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 个连续的内存像素并将两个图像映射到其中的一维内存块,我可以在纹理内按如下方式排列图像: 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 提供的矩阵变换以某种方式实现此映射?
我还没有尝试过这个,但我已经想把它作为一个想法扔出去了:
更新:我现在尝试了它,只需稍作改动就可以很好地工作(见评论)!
假设我的大纹理的宽度为 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
而不是达到预期的结果。)
这听起来是否合理,或者有人可以在我继续尝试实施之前发现这方面的问题吗? (可能需要几天时间,因为我必须先做一些其他事情才能获得可测试的应用程序...)