glTexSubImage2D按像素移动NSImage

时间:2014-06-19 16:14:31

标签: macos cocoa opengl

我正在开发一款可以创建自己的纹理图集的应用。地图集上的元素大小不一,但是以网格模式放置。

除了我使用新元素(来自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报道),这是我一直在使用的测试图像: enter image description here

这是我的应用程序中结果的屏幕抓​​取:

enter image description here

我是否正确打开图像包装......?我知道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);

1 个答案:

答案 0 :(得分:1)

您可能遇到过我在此处已回答的问题:stackoverflow.com/a/5879551/524368

它并不是关于像素坐标,而是纹素的像素完美寻址。这对于纹理图集尤其重要。一个常见的误解是,许多人认为纹理坐标0和1恰好位于像素中心。但在OpenGL中并非如此,纹理坐标0和1恰好位于纹理包裹的像素之间的边界上。如果构建纹理图集使得0和1处于像素中心假设,那么在OpenGL中使用相同的寻址方案将导致图像模糊或像素移位。你需要考虑到这一点。

  

我仍然不明白这对于正在渲染的纹理的子部分有何影响。

理解OpenGL纹理不是图像而是支持插值器的样本(因此&#34;采样器&#34;着色器中的制服),这有很大帮助。因此,为了获得看起来非常清晰的图像,您必须选择以某种方式进行采样的纹理坐标,以便插值器在处精确评估支持样本的位置。然而,这些样本的位置既不是整数坐标也不是简单的分数(i / N)。

请注意,较新版本的GLSL提供纹理采样函数texelFetch,它完全绕过插值器并直接寻址纹理像素。如果您需要像素完美纹理,您可能会发现这更容易使用(如果可用)。