使用着色器和rtt复制纹理子矩形

时间:2013-08-21 22:23:41

标签: c++ opengl

我需要编写一个函数,该函数将从2D纹理(非幂2)中获取子矩形,并使用着色器将其复制到输出2D纹理的目标子矩形(无glSubImage或类似)。 源和目标也可能没有相同的大小,所以我需要使用线性过滤(甚至是mipmap)。

void CopyToTex(GLuint dest_tex,GLuint src_tex,
               GLuint src_width,GLuint src_height,
               GLuint dest_width,GLuint dest_height,
               float srcRect[4],
               GLuint destRect[4]);

这里srcRect是标准化的0-1坐标,即矩形[0,1] x [0,1]触摸输入纹理的每个边界像素的中心。

要在输入和源尺寸不匹配时获得良好效果,我想使用GL_LINEAR过滤。

我希望这个函数以连贯的方式运行,即用多个子函数多次调用它会产生与一个带有子函数联合的调用相同的结果;也就是说,线性采样器应该对输入像素的精确中心进行采样。

此外,如果输入矩形恰好适合目标矩形,则应该发生精确复制。 这似乎特别困难。

我现在得到的是这样的:

//Setup RTT, filtering and program

float vertices[4] = { 
      float(destRect[0]) / dest_width * 2.0 - 1.0,
      float(destRect[1]) / dest_height * 2.0 - 1.0, 
      //etc..
};

float texcoords[4] = {
      (srcRect[0] * (src_width - 1) + 0.5) / src_width - 0.5 / dest_width,
      (srcRect[1] * (src_height - 1) + 0.5) / src_height - 0.5 / dest_height,
      (srcRect[2] * (src_width - 1) + 0.5) / src_width + 0.5 / dest_width,
      (srcRect[3] * (src_height - 1) + 0.5) / src_height + 0.5 / dest_height,
};

glBegin(GL_QUADS);
glTexCoord2f(texcoords[0], texcoords[1]);
glVertex2f(vertices[0], vertices[1]);

glTexCoord2f(texcoords[2], texcoords[1]);
glVertex2f(vertices[2], vertices[1]);

//etc...
glEnd();

为了编写此代码,我按照this页面中的信息。

这似乎在某些角落情况下正常工作(精确复制,复制一个像素的行或列)。

我最难的测试用例是当输入和输出纹理都大于2xN时执行2xN矩形的精确副本。

我可能在偏移和缩放方面遇到一些问题(琐碎的不起作用)。

1 个答案:

答案 0 :(得分:0)

解决方案:

texcoords定义中的0.5/tex_width部分是错误的。 一种简单的解决方法是完全删除该部分。

float texcoords[4] = {
      (srcRect[0] * (src_width - 1) + 0.5) / src_width,
      (srcRect[1] * (src_height - 1) + 0.5) / src_height,
      (srcRect[2] * (src_width - 1) + 0.5) / src_width,
      (srcRect[3] * (src_height - 1) + 0.5) / src_height
};

相反,我们通过偏移顶点来绘制一个较小的四边形:

float dx = 1.0 / (dest_rect[2] - dest_rect[0]) - epsilon;
float dy = 1.0 / (dest_rect[3] - dest_rect[1]) - epsilon;

// assume glTexCoord for every vertex 
glVertex2f(vertices[0] + dx, vertices[1] + dy);
glVertex2f(vertices[2] - dx, vertices[1] + dy);
glVertex2f(vertices[2] - dx, vertices[3] - dy);
glVertex2f(vertices[0] + dx, vertices[3] - dy);

通过这种方式,我们绘制了一个从每个边界像素的精确中心传递的四边形。 由于在这种情况下OpenGL可能会也可能不会绘制边框像素,因此我们需要epsilons。

我相信我的原始解决方案(不要偏移顶点坐标)仍然可以工作,但需要一些额外的数学计算texcoords的正确偏移。