【问题标题】:OpenGL Texture Mapping by Pixel Region像素区域的OpenGL纹理映射
【发布时间】: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 .

【问题讨论】:

    标签: opengl 2d texture-mapping opentk


    【解决方案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 版本,您还可以直接在着色器中查询纹理大小,这样就可以完全不使用制服。
    猜你喜欢
    • 2013-10-02
    • 2011-05-04
    • 2011-03-16
    • 1970-01-01
    • 2011-08-11
    • 2011-08-25
    • 2012-07-28
    • 2011-05-16
    • 2014-12-30
    相关资源
    最近更新 更多