macos - glTexSubImage2D 将 NSImage 移动一个像素

标签 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、8 位(由 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);

最佳答案

您可能遇到了我已经在这里回答的问题:stackoverflow.com/a/5879551/524368

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

I still don't understand how that makes a difference to a sub-section of the texture that's being rendered.

理解 OpenGL 纹理与其说是图像,不如说是插值器的支持样本(因此着色器中的“采样器”制服)很有帮助。因此,为了获得真正清晰的图像,您必须以某种方式选择要从中采样的纹理坐标,以便插值器准确地在支持样本的位置进行评估。然而,这些样本的位置既不是整数坐标也不是简单的分数 (i/N)。

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

关于macos - glTexSubImage2D 将 NSImage 移动一个像素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24311419/

相关文章:

opengl - glMapBuffer()和glBuffers,使用(void *)访问如何与硬件一起工作?

objective-c - 如何将所有错误(包括未捕获的异常、NSLog 调用和其他日志)重定向到 Mac OS X 上的日志文件?

macos - 简单的 macOS 程序集 64 位程序的堆栈对齐不正确

cocoa - 启动时打开网址

macos - 编辑框架中的方法

c++ - "objects map"在 opengl 中

cocoa - NSUserDefaults objectForKey 输出比较不起作用

php - 在 Mac OS 10.6 (Snow Leopard)、10.7 (Lion)、10.8 (Mountain Lion) 上激活 PHP 和 MySQL 的最简单方法?

cocoa + 我应该使用什么 ui 元素?

c++ - 调试绘制 Box2D 与 Testbed 应用程序相比非常大