我正在开发一款可以创建自己的纹理图集的应用。地图集上的元素大小不一,但是以网格模式放置。
除了我使用新元素(来自NSImage的数据)在地图集的部分上书写时,图像向右移动了一个像素,这一切都正常。
我用来将像素写入地图册的代码是:
-(void)writeToPlateWithImage:(NSImage*)anImage atCoord:(MyGridPoint)gridPos;
{
static NSSize insetSize; //ultimately this is the size of the image in the box
static NSSize boundingBox; //this is the size of the box that holds the image in the grid
static CGFloat multiplier;
multiplier = 1.0;
NSSize plateSize = NSMakeSize(atlas.width, atlas.height);//Size of entire atlas
MyGridPoint _gridPos;
//make sure the column and row position is legal
_gridPos.column= gridPos.column >= m_numOfColumns ? m_numOfColumns - 1 : gridPos.column;
_gridPos.row = gridPos.row >= m_numOfRows ? m_numOfRows - 1 : gridPos.row;
_gridPos.column = gridPos.column < 0 ? 0 : gridPos.column;
_gridPos.row = gridPos.row < 0 ? 0 : gridPos.row;
insetSize = NSMakeSize(plateSize.width / m_numOfColumns, plateSize.height / m_numOfRows);
boundingBox = insetSize;
//…code here to calculate the size to make anImage so that it fits into the space allowed
//on the atlas.
//multiplier var will hold a value that sizes up or down the image…
insetSize.width = anImage.size.width * multiplier;
insetSize.height = anImage.size.height * multiplier;
//provide a padding around the image so that when mipmaps are created the image doesn’t ‘bleed’
//if it’s the same size as the grid’s boxes.
insetSize.width -= ((insetSize.width * (insetPadding / 100)) * 2);
insetSize.height -= ((insetSize.height * (insetPadding / 100)) * 2);
//roundUp() is a handy function I found somewhere (I can’t remember now)
//that makes the first param a multiple of the the second..
//here we make sure the image lines are aligned as it’s a RGBA so we make
//it a multiple of 4
insetSize.width = (CGFloat)roundUp((int)insetSize.width, 4);
insetSize.height = (CGFloat)roundUp((int)insetSize.height, 4);
NSImage *insetImage = [self resizeImage:[anImage copy] toSize:insetSize];
NSData *insetData = [insetImage TIFFRepresentation];
GLubyte *data = malloc(insetData.length);
memcpy(data, [insetData bytes], insetData.length);
insetImage = NULL;
insetData = NULL;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, atlas.textureIndex);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //have also tried 2,4, and 8
GLint Xplace = (GLint)(boundingBox.width * _gridPos.column) + (GLint)((boundingBox.width - insetSize.width) / 2);
GLint Yplace = (GLint)(boundingBox.height * _gridPos.row) + (GLint)((boundingBox.height - insetSize.height) / 2);
glTexSubImage2D(GL_TEXTURE_2D, 0, Xplace, Yplace, (GLsizei)insetSize.width, (GLsizei)insetSize.height, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
free(data);
glBindTexture(GL_TEXTURE_2D, 0);
glGetError();
}
图像是RGBA,8bit(由PhotoShop报道),这是我一直在使用的测试图像:
这是我的应用程序中结果的屏幕抓取:
我是否正确打开图像包装......?我知道resizeImage:
函数有效,因为我已将结果保存到磁盘并绕过它,因此问题出现在gl代码中......
编辑:只是为了澄清,正在渲染的图集部分比框图大。因此,使用glTexSubImage2D
写入的区域正在发生转变。
编辑2:最后,通过偏移进入地图集部分的复制数据进行排序。
我不完全理解为什么会这样,也许这是一个黑客而不是一个正确的解决方案,但现在就是。
//resize the image to fit into the section of the atlas
NSImage *insetImage = [self resizeImage:[anImage copy] toSize:NSMakeSize(insetSize.width, insetSize.height)];
//pointer to the raw data
const void* insetDataPtr = [[insetImage TIFFRepresentation] bytes];
//for debugging, I placed the offset value next
int offset = 8;//it needed a 2 pixel (2 * 4 byte for RGBA) offset
//copy the data with the offset into a temporary data buffer
memcpy(data, insetDataPtr + offset, insetData.length - offset);
/*
.
. Calculate it's position with the texture
.
*/
//And finally overwrite the texture
glTexSubImage2D(GL_TEXTURE_2D, 0, Xplace, Yplace, (GLsizei)insetSize.width, (GLsizei)insetSize.height, GL_RGBA, GL_UNSIGNED_BYTE, data);
答案 0 :(得分:1)
您可能遇到过我在此处已回答的问题:stackoverflow.com/a/5879551/524368
它并不是关于像素坐标,而是纹素的像素完美寻址。这对于纹理图集尤其重要。一个常见的误解是,许多人认为纹理坐标0和1恰好位于像素中心。但在OpenGL中并非如此,纹理坐标0和1恰好位于纹理包裹的像素之间的边界上。如果构建纹理图集使得0和1处于像素中心假设,那么在OpenGL中使用相同的寻址方案将导致图像模糊或像素移位。你需要考虑到这一点。
我仍然不明白这对于正在渲染的纹理的子部分有何影响。
理解OpenGL纹理不是图像而是支持插值器的样本(因此&#34;采样器&#34;着色器中的制服),这有很大帮助。因此,为了获得看起来非常清晰的图像,您必须选择以某种方式进行采样的纹理坐标,以便插值器在处精确评估支持样本的位置。然而,这些样本的位置既不是整数坐标也不是简单的分数(i / N)。
请注意,较新版本的GLSL提供纹理采样函数texelFetch
,它完全绕过插值器并直接寻址纹理像素。如果您需要像素完美纹理,您可能会发现这更容易使用(如果可用)。