OpenGL:如何在指定UV坐标时避免舍入错误

时间:2010-02-27 04:29:22

标签: c opengl floating-point

我正在使用OpenGL编写2D游戏。当我想将纹理的一部分作为精灵blit时,我使用glTexCoord2f(u,v)指定UV坐标,u和v计算如下:

GLfloat u = (GLfloat)xpos_in_texture/(GLfloat)width_of_texture;
GLfloat v = (GLfloat)ypos_in_texture/(GLfloat)height_of_texture;

除非我使用glScale来放大或缩小游戏,否则大部分时间都可以正常工作。然后浮点舍入误差会导致一些像素在纹理内的预期矩形的右侧或下方绘制。

可以做些什么呢?目前我正在从矩形的右边缘和底边缘减去一个'epsilon'值,它似乎工作但这看起来像一个可怕的kludge。有没有更好的解决方案?

4 个答案:

答案 0 :(得分:3)

您的问题很可能不是来自舍入错误,而是对OpenGL如何将纹素映射到像素的误解。如果您注意到一个一个错误,可能是因为您的UV,顶点位置或投影矩阵/视口对未与它们应该的位置对齐。

为了简化,我将简单地讨论1D,并假设您使用投影和视口将X,Y坐标映射到等效像素位置(即glOrtho(0,width,0,height,zmin,zmax)glViewport(0,width,0,height)

假设你要绘制5个纹素(从0开始,为简单起见),你的64宽纹理显示在从像素20开始的10个像素(2的比例)上。

要到达那里,绘制X坐标20和30以及U(UV对)10/64和15/64的三角形。 OpenGL的光栅化将生成10个像素的阴影,X坐标为20.5,21.5,... 29.5。 请注意,这些位置不是完整整数。 OpenGL在像素的中间光栅化。

同样,它将产生10.25 / 64,10.75 / 64,11.25 / 64,11.75 / 64 ...... 14.25 / 64,14.75 / 64的U坐标。再次注意纹理坐标,纹理空间中的纹素位置,不是完整的整数。来自texel位置中间的OpenGL样本,所以这很好。

采样器如何使用这些UV来生成纹理元素值取决于滤波模式,但最接近或线性,像素应仅包含在感兴趣的纹素内(0.25的大小为0.5应仅使用0到0之间的颜色0.5,这都在第一个纹素内。)

一般来说,如果你遵循我列出的一般原则,你就不应该看到文物。

  1. 使用正好与帧缓冲区大小相同的正交和视口
  2. 正确使用X,X +宽度的位置
  3. 使用与您想要的纹素完全对应的UV(如果您想要从纹素0开始的10个纹素,请使用U = 0到U = 10.
  4. 如果你的数学中某处有-1,那么它可能不正确(对于位置或UV)。

    回到你的例子,你不清楚如何将你计算的uv与位置联系起来(因为你没有显示位置计算)。 还不清楚你是如何得到xpos_in_texture的(你应该解释你是如何为精灵的角点计算它们的)。我的猜测是你计算错了。

答案 1 :(得分:2)

有点晚了,但是对于后人我遇到了同样的问题,在缩放或缩放视图时,纹理图集的相邻区域的像素会变成精灵/图块。我的glOrtho,glViewport等尺寸都设置正确,然后我意识到问题是我在翻译相机之前缩放了视图,这意味着即使我在缩放之前捕捉整数像素,在缩放之后它会对齐到像素的一小部分并引入纹素问题。

因此,如果你的代码看起来像这样,那么 camera.zoom 是一个非整数(即0.75):

glScalef(camera.zoom, camera.zoom, 1.0f);
glTranslatef(camera.x, camera.y, 0.0f);

您需要确保缩放后的平移结果与屏幕上的整个像素对齐,这样您就可以执行以下操作:

glScalef(camera.zoom, camera.zoom, 1.0f);
glTranslatef(
    floor(camera.x * camera.zoom) / camera.zoom,
    floor(camera.y * camera.zoom) / camera.zoom,
    0.0f);

答案 2 :(得分:0)

将除法作为double,将结果向下自己调整到所需的精度级别,然后将其转换为GLFloat。

答案 3 :(得分:-1)

你的xpos / ypos必须基于0到(宽度或高度) - 1然后:

GLfloat u = (GLfloat)xpos_in_texture/(GLfloat)(width_of_texture - 1);
GLfloat v = (GLfloat)ypos_in_texture/(GLfloat)(height_of_texture - 1);