用于渲染精灵的纹理采样坐标

Posted

技术标签:

【中文标题】用于渲染精灵的纹理采样坐标【英文标题】:Texture Sampling Coordinates to Render a Sprite 【发布时间】:2012-07-12 12:45:17 【问题描述】:

假设我们有一个纹理(在本例中为 8x8 像素),我们想将其用作精灵表。其中一个子图像(精灵)是纹理内部 4x3 的子区域,如下图所示:

(显示四个角的归一化纹理坐标)

现在,基本上有两种方法可以将纹理坐标分配给 4px x 3px 大小的四边形,以便它有效地成为我们正在寻找的精灵;第一个也是最直接的方法是在子区域的拐角处对纹理进行采样:

// Texture coordinates

GLfloat sMin = (xIndex0                  ) / imageWidth;
GLfloat sMax = (xIndex0 + subregionWidth ) / imageWidth;
GLfloat tMin = (yIndex0                  ) / imageHeight;
GLfloat tMax = (yIndex0 + subregionHeight) / imageHeight;

虽然在第一次实现此方法时,ca. 2010 年,我意识到精灵看起来有点“扭曲”。经过一番搜索,我在 cocos2d 论坛上看到了一篇文章,解释了在渲染精灵时对纹理进行采样的“正确方法”是这样的:

// Texture coordinates

GLfloat sMin = (xIndex0                   + 0.5) / imageWidth;
GLfloat sMax = (xIndex0 + subregionWidth  - 0.5) / imageWidth;
GLfloat tMin = (yIndex0                   + 0.5) / imageHeight;
GLfloat tMax = (yIndex0 + subregionHeight - 0.5) / imageHeight;

...在修复了我的代码之后,我高兴了一阵子。但在此过程中的某个地方,我相信是在 ios 5 推出前后,我开始觉得我的精灵看起来不太好。经过一些测试,我切换回“蓝色”方法(第二张图片),现在它们看起来不错,但并非总是

我是不是疯了,或者 iOS 5 与 GL ES 纹理映射相关的东西发生了变化?也许我做错了什么? (例如,顶点位置坐标略微偏离?错误的纹理设置参数?)但是我的代码库没有改变,所以也许我从一开始就做错了什么......?

我的意思是,至少在我的代码中,感觉好像“红色”方法过去是正确的,但现在“蓝色”方法提供了更好的结果。

现在,我的游戏看起来不错,但我觉得我迟早必须修复一些半错​​误的地方......

有什么想法/经验/意见吗?

附录

为了渲染上面的精灵,我会在正交投影中绘制一个 4x3 的四边形,每个顶点都分配了前面提到的代码中隐含的纹理坐标,如下所示:

// Top-Left Vertex
 sMin, tMin ;

// Bottom-Left Vertex
 sMin, tMax ;

// Top-Right Vertex
 sMax, tMin ;

// Bottom-right Vertex
 sMax, tMax ;

原始四边形是从 (-0.5, -0.5) 到 (+0.5, +0.5) 创建的;即它是屏幕中心的一个单位正方形,然后缩放到子区域的大小(在本例中为 4x3),其中心位于整数 (x,y) 坐标处。我闻到这也有关系,尤其是当宽度、高度或两者都不均匀时?

附录 2

我也找到了这篇文章,但我仍在尝试整理(这里是凌晨 4:00) http://www.mindcontrol.org/~hplus/graphics/opengl-pixel-perfect.html

【问题讨论】:

评论:我个人认为纹理的行为在 iOS 4 和 iOS 5 之间没有改变(它应该是 GL ES 1.1/2.0 规范的一部分);只是我从我的经历中得到了这种印象。一定是错的,我知道,但还是很疑惑…… 【参考方案1】:

这张图片比我们看到的要多一些,纹理坐标并不是纹理被采样的唯一因素。在你的情况下,我相信蓝色可能是你想要的。

您最终想要的是对中心的每个纹素进行采样。您不想在两个纹素之间的边界上采样,因为这要么将它们与 线性 采样相结合,要么任意选择一个或另一个与 nearest,具体取决于浮点计算以哪种方式循环。

话虽如此,您可能会认为您不希望将纹理坐标设置在 (0,0)、(1,1) 和其他角,因为它们位于纹理边界上。然而需要注意的重要一点是,opengl 在片段中心对纹理进行采样。

举一个超级简单的例子,考虑一个 2 x 2 像素的显示器,具有 2 x 2 像素的纹理。

如果您从 (0,0) 到 (2,2) 绘制一个四边形,这将覆盖 4 个像素。如果您对该四边形进行纹理映射,则需要从纹理中获取 4 个样本。

如果您的纹理坐标从 0 变为 1,则 opengl 将对其进行插值并从每个像素的中心采样,左下角的 texcoord 从左下角像素的左下角开始。这最终将生成 (0.25, 0.25)、(0.75,0.75)、(0.25, 0.75) 和 (0.75, 0.25) 的 texcoord 对。这会将样本放在每个纹素的中间,这正是您想要的。

如果您像红色示例中那样将纹理坐标偏移半个像素,那么它将错误地插值,并且您最终会从纹理像素的中心对纹理进行采样。

长话短说,您要确保像素与纹素正确对齐(不要在非整数像素位置绘制精灵),并且不要随意缩放精灵。

如果蓝色方块给你的结果不好,你能举个例子,或者描述一下你是怎么画的吗?

图片说1000字:

【讨论】:

我不明白你要推荐哪一个;首先,您建议“在中心对每个纹素进行采样”,然后您建议使用蓝色方法?当我指定 0.0 到 1.0 时,为什么 openGL 会从 0.27 采样到 0.75?我以为我必须自己指定每个纹素中心的坐标(红色示例),并且边界本身在 0.0 和 1.0? 另外,如果我指定(比如)0.0 的 tex 坐标,它将在纹理图像中的第一个像素和“外部”(黑色 - 假设没有换行)之间进行采样,即“ '最近'模式下的颜色'或'黑色',以及'线性'模式下的混合(你似乎也提到了)......? @ranReloaded 我添加了一张图片,希望能更好地解释它。让我知道这是否更有意义。 你就是男人!所以底线是:我不需要将 0.5/(side size) 偏移量添加到 tex 坐标中,因为它已经在光栅/片段阶段考虑了。现在很有意义:即使顶点位于网格的边缘,纹理也会根据每个片段的中心进行采样。 是的,听起来像。如果您说您正在绘制一个从 -0.5 到 0.5 的四边形,然后对其进行缩放,它可能最终会出现在非像素边界上。只需确保所有四边形顶点都位于整数像素边缘上。

以上是关于用于渲染精灵的纹理采样坐标的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL4 多重采样抗锯齿和渲染到纹理

从也是渲染目标的纹理中采样

如何在多采样纹理上渲染帧缓冲区对象?

你能改变金属着色器中采样器的边界吗?

金属着色器纹理读取与示例

Cg入门21:Fragment shader - 2D纹理采样