像素区域的OpenGL纹理映射
Posted
技术标签:
【中文标题】像素区域的OpenGL纹理映射【英文标题】:OpenGL Texture Mapping by Pixel Region 【发布时间】:2015-01-04 03:42:17 【问题描述】:假设您有一个简单的游戏 Tileset,如下所示:
现在,当处理一个简单的 GUI 框架(如 .NET)时,加载该图像、选择其中的一部分,然后逐块绘制它会相当容易。但是,当使用 OpenGL 时,这个过程似乎有点……呃,独特的。我可以轻松地将该图像加载到 OpenGL 中并将其绑定到某些几何图形,但是,当涉及到“选择”某些图块(没有解除绑定所述纹理)时,它似乎需要与传统的 x、y、width 不同的数学类型,高度接近。
如果我想为当前图块选择 64,128(像素空间中的坐标)的图块,我会使用反映理想的纹理坐标,而不是我在其他网站上看到人们建议的这些奇怪的分数。
似乎OpenGL在绑定纹理时根本不使用像素空间,或者我可能误解了这里的一些基本概念;我不确定。
这是一种在任意位置渲染 32 x 32 瓦片的简单方法(在本示例中将保持在 0,0):
int spriteX = 0;
int spriteY = 0;
int spriteWidth = 32;
int spriteHeight = 32;
GL.BindTexture( TextureTarget.Texture2D , texture );
GL.Begin( PrimitiveType.Quads );
GL.TexCoord2( 0 , 1 );
GL.Vertex2( spriteX , spriteY );
GL.TexCoord2( 1 , 1 );
GL.Vertex2( spriteX + spriteWidth , spriteY );
GL.TexCoord2( 1 , 0 );
GL.Vertex2( spriteX + spriteWidth , spriteY + spriteHeight );
GL.TexCoord2( 0 , 0 );
GL.Vertex2( spriteX , spriteY + spriteHeight );
GL.End();
在任何人抱怨即时模式之前:我完全意识到它已被弃用;我不打算将它用于任何类型的成品。
我不会用整个纹理(上面的代码正在做的)映射绘制的四边形,而是告诉它只映射图像的一个区域,比如:X 64,Y 128,宽度 32,高度 32 .
【问题讨论】:
【参考方案1】:最直接的方法是使用您所说的“这些奇怪的分数”。他们并不是真的那么奇怪。它们只是……分数。
假设您的整个纹理图集是 1024x1024,并且您想要具有指定尺寸(X 64、Y 128、宽度 32、高度 32)的纹理,则纹理坐标为:
left: X / 1024 = 64.0f / 1024.0f
right: (X + Width) / 1024 = 96.f / 1024.0f
top = Y / 1024 = 128.0f / 1024.0f
bottom = (Y + Height) / 1024 = 160.0f / 1024.f
另一种方法是指定纹理坐标的变换,在这种情况下是缩放变换。使用固定管道,它看起来像这样:
glMatrixMode(GL_TEXTURE);
glScalef(1.0f / 1024.0f, 1.0f / 1024.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
然后您可以使用纹理图集中的像素位置指定纹理坐标。
使用可编程流水线,上述内容已过时。但是您可以通过将统一值传递给着色器来轻松应用相同类型的缩放,然后将其与纹理坐标相乘。
在顶点着色器中,它可能如下所示:
uniform vec2 TexCoordScale;
in vec2 TexCoord;
out vec2 FragTexCoord;
...
FragTexCoord = TexCoordScale * TexCoord;
然后在片段着色器中,您有匹配的 FragTexCoord
in
变量,并将其用于您的纹理采样操作。
在客户端代码中,你设置统一:
GLint texCoordScaleLoc = glGetUniformLocation(program, "TexCoordScale");
glUniform2f(texCoordScaleLoc, 1.0f / 1024.0f, 1.0f / 1024.0f);
并像往常一样为纹理坐标设置顶点属性,只是现在可以使用像素坐标而不是“奇怪的分数”。
【讨论】:
如果我使用缩放转换(像梦一样工作)会有任何明显的性能损失吗? 我怀疑会有可衡量的差异。 @Krythic:您应该只知道纹理矩阵 - 作为矩阵堆栈的其余部分和整个固定函数管道 - 与立即模式一样被弃用。但是,将其移植到可编程着色器是直截了当的——您甚至不需要完整的 4x4 矩阵的开销,一个简单的 2D 向量乘法就足够了。 @derhass 如果你有时间,你认为你可以写一个这样的例子吗?我很感兴趣。 @Krythic:嗯,在什么情况下?您对可编程管道和 GLSL 着色器编程的熟悉程度如何?添加这个比例因子归结为添加uniform vec2 texScale;
,然后在对纹理进行采样时通过简单地将texcoords乘以该向量来应用它。在客户端,您只需将制服设置为(1.0/width,1.0/height)
。使用相当现代的 GLSL 版本,您还可以直接在着色器中查询纹理大小,这样就可以完全不使用制服。以上是关于像素区域的OpenGL纹理映射的主要内容,如果未能解决你的问题,请参考以下文章