【问题标题】:Distortion with 'pixel accurate' OpenGL rendering of sprites精灵的“像素精确”OpenGL渲染失真
【发布时间】:2011-06-14 20:38:16
【问题描述】:

要定义我要做什么:我希望能够从 ^2x^2 大小的 PNG 中获取任意“精灵”图像,并仅显示感兴趣的像素到给定的 x/y 位置屏幕。

我的结果are the problem - major distortion - 看起来很糟糕! (请注意,这些 SS 在 iPhone sim 中,但在真正的视网膜设备上,它们看起来是一样的……垃圾)。这是“预览”中源 PNG 的屏幕截图 - which looks wonderful(我在这个问题中描述的任何渲染变化看起来几乎和垃圾一样)

以前,我 asked a question 曾讨论过使用 OpenGL ES 2.0 将非 2 次幂纹理显示为精灵(尽管这适用于任何 OpenGL)。我很接近,但我有一些我无法解决的问题。我认为可能存在多个错误 - 我认为存在一些错误,我基本上通过渲染大然后压缩 x2 或反之亦然来混淆我正在显示的内容,但我看不到它。此外,还有一个错误,我无法处理它们。我无法直观地识别它们的发生,但我确定它们就在那里。

我在 960 x 640 横向(在 iPhone4 视网膜显示屏上)工作。所以我预计 0->959 从左到右移动,0->639 从下到上移动。 (而且我认为我看到的是相反的 - 但这不是这个问题的目的)

为了让事情变得简单,我在这个测试用例中试图实现的是一个 PNG 文件的全屏 960x640 显示。只是其中之一。我先显示一个红色背景,这样我是否覆盖屏幕就很明显了。

更新:我意识到 setFramebuffer 调用中的“glViewport”正在向后设置我的宽度和高度。我注意到了这一点,因为当我将几何图形设置为从 0,0 到 100,100 时,它绘制的是矩形而不是正方形。当我交换这些时,该调用确实绘制了一个正方形。但是,使用相同的调用,我的整个屏幕填充了 0,0 -> 480,320(一半“分辨率”)的顶点范围......不明白这一点。但是,无论我从哪里推进,我仍然没有得到一个好看的结果

这是我的顶点着色器:

attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
// Gives 'landscape' full screen..
mat4 projectionMatrix = mat4( 2.0/640.0, 0.0, 0.0, -1.0,
                              0.0, 2.0/960.0, 0.0, -1.0,
                              0.0, 0.0, -1.0, 0.0,
                              0.0, 0.0, 0.0, 1.0);  
// Gives a 1/4 of screen.. (not doing 2.0/.. was suggested in previous SO Q)
/*mat4 projectionMatrix = mat4( 1.0/640.0, 0.0, 0.0, -1.0,
                              0.0, 1.0/960.0, 0.0, -1.0,
                              0.0, 0.0, -1.0, 0.0,
                              0.0, 0.0, 0.0, 1.0);                        */

// Apply the projection matrix to the position and pass the texCoord 
void main()
{
    gl_Position = a_position;
    gl_Position *= projectionMatrix;

    v_texCoord = a_texCoord;
}

这是我的片段着色器:

precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D s_texture;


void main()
{
    gl_FragColor = texture2D(s_texture, v_texCoord);
}

这是我的绘图代码:

#define MYWIDTH 960.0f
#define MYHEIGHT 640.0f

// I have to refer to 'X' as height although I'd assume I use 'Y' here..
// I think my X and Y throughout this whole block of code is screwed up
// But, I have experimented flipping them all and verifying that if they
// Are taken from the way they're set now to swapping X and Y that things
// end up being turned the wrong way.  So this is a mess, but unlikely my problem
#define BG_X_ORIGIN 0.0f
// ALSO NOTE HERE: I have to put my 'dest' at 640.0f.. --- see note [1] below
#define BG_X_DEST 640.0f

#define BG_Y_ORIGIN 0.0f
// --- see note [1] below
#define BG_Y_DEST 960.0f

// These are texturing coordinates, I texture starting at '0' px and then
// I calculate a percentage of the texture to use based on how many pixels I use
// divided by the actual size of the image (1024x1024)  
#define BG_X_ZERO   0.0f
#define BG_Y_USEPERCENTAGE BG_X_DEST / 1023.0f 

#define BG_Y_ZERO 0.0f
#define BG_X_USEPERCENTAGE BG_Y_DEST / 1023.0f

// glViewport(0, 0, MYWIDTH, MYHEIGHT); 
// See note 2.. it sets glViewport basically, provided by Xcode project template
[(EAGLView *)self.view setFramebuffer];

// Big hack just to get things going - like I said before, these could be backwards
// w/respect to X and Y 
static const GLfloat backgroundVertices[] = {
    BG_X_ORIGIN, BG_Y_ORIGIN, 
    BG_X_DEST, BG_Y_ORIGIN, 
    BG_X_ORIGIN, BG_Y_DEST, 
    BG_X_DEST, BG_Y_DEST 
};


static const GLfloat backgroundTexCoords[] = {
    BG_X_ZERO, BG_Y_USEPERCENTAGE,
    BG_X_USEPERCENTAGE, BG_Y_USEPERCENTAGE,
    BG_X_ZERO, BG_Y_ZERO,
    BG_X_USEPERCENTAGE, BG_Y_ZERO

};  

    // Turn on texturing
glEnable(GL_TEXTURE_2D);

// Clear to RED so that it's obvious when I'm not drawing my sprite on screen
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);


    // Texturing parameters - these make sense.. don't think they are the issue
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);//GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);//GL_LINEAR);

    // Update attribute values.
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, backgroundVertices);
    glEnableVertexAttribArray(ATTRIB_VERTEX);
    glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, 0, 0, backgroundTexCoords);
    glEnableVertexAttribArray(ATTRIB_TEXCOORD);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, background->textureId);         

    // I don't understand what this uniform does in the texture2D call in shader.
    glUniform1f(uniforms[UNIFORM_SAMPLERLOC], 0);

    // Draw the geometry...
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // present the framebuffer see note [3]
    [(EAGLView *)self.view presentFramebuffer];

注意[1]

如果我将 BG_X_DEST 设置为 639.0f,我无法完全覆盖 640 像素,我会在右侧显示红色。但这对我来说没有意义——我的目标是像素完美,我必须从 0 到 640 绘制我的精灵几何图形,当我只有 640 像素时,它是 641 像素!!! red line appearing with 639f instead of 640f

如果我将 BG_Y_DEST 设置为 959.0f,我不会通过红线显示。 red line top bug appearing with 958f instead of 960 or 959f

这可能是我正在处理的错误的一个很好的线索。

注意:[2] - 包含在 Xcode 的 OpenGL ES 2 框架中

- (void)setFramebuffer 
{
    if (context)
    {
        [EAGLContext setCurrentContext:context];

        if (!defaultFramebuffer)
            [self createFramebuffer];

        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);

        glViewport(0, 0, framebufferWidth, framebufferHeight);
    }
}

注 [3]: - 包含在 Xcode 的 OpenGL ES 2 框架中

- (BOOL)presentFramebuffer
{
    BOOL success = FALSE;

    if (context)
    {
        [EAGLContext setCurrentContext:context];

        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);

        success = [context presentRenderbuffer:GL_RENDERBUFFER];
    }

    return success;
}

注意 [4] - 相关的图像加载代码(我使用了带和不带 alpha 通道的 PNG,实际上它似乎没有任何区别......我也尝试过改变我的代码最多为 ARGB 而不是 RGBA,这是错误的 - 因为 A = 1.0 到处都是,我得到一个非常红色的图像,这让我认为 RGBA 实际上是有效的,这个代码是正确的。):更新: 我已经使用 CG/ImageIO 调用将此纹理加载切换到完全不同的设置,它看起来与此相同,所以我认为这不是图像库完成的某种混叠或颜色压缩(除非它们都采用相同的基础调用,这是可能的..)

// Otherwise it isn't already loaded
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);//GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);//GL_LINEAR);


// TODO Next 2 can prob go later on..
glGenTextures(1, &(newTexture->textureId)); // generate Texture
// Use this before 'drawing' the texture to the memory...
glBindTexture(GL_TEXTURE_2D, newTexture->textureId);


NSString *path = [[NSBundle mainBundle] 
                        pathForResource:[NSString stringWithUTF8String:newTexture->filename.c_str()] ofType:@"png"];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
if (image == nil)
    NSLog(@"Do real error checking here");

newTexture->width = CGImageGetWidth(image.CGImage);
newTexture->height = CGImageGetHeight(image.CGImage);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

void *imageData = malloc(newTexture->height * newTexture->width * 4 );

CGContextRef myContext = CGBitmapContextCreate
    (imageData, newTexture->width, newTexture->height, 8, 4 * newTexture->width, colorSpace, 
    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );

CGColorSpaceRelease(colorSpace);
CGContextClearRect(myContext, CGRectMake(0, 0, newTexture->width, newTexture->height));

CGContextDrawImage(myContext, CGRectMake(0, 0, newTexture->width, newTexture->height), image.CGImage);


// Texture is created!
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newTexture->width, newTexture->height, 0, 
                 GL_RGBA, GL_UNSIGNED_BYTE, imageData);
CGContextRelease(myContext);

free(imageData);
[image release];
[texData release];

【问题讨论】:

  • 我还没有完成所有代码,但是 BG_Y_USEPERCENTAGE 和 BG_X_USEPERCENTAGE 除以 1023.0 看起来很可疑。这应该真的除以1024.0。 BG_X_DEST 和 BG_Y_DEST 正确为 640 和 960。这是有道理的,因为它们是宽度/高度,而不是右/下坐标
  • 因此,如果我已经为正交设置了矩阵,并且我正在绘制一个“四边形”,我想用精灵图像逐个像素 1:1 纹理.. 如果我希望它是 960 像素宽和 640 像素高,我将绘制角为 0,0 的四边形; 0,960; 640,960; 640,0?如果我想把它画成 10x10 像素,0,0; 0,10; 10,10; 10, 0?所以我会画+1?这似乎违反直觉 - 为什么?我将使用什么坐标在偏移 10,10 处绘制一个 50x40 像素的精灵?我没有提到,我已经尝试了 w/1024f,这给出了几乎相同的结果。但这可能是我想修复的 off-by-1 情况,所以请详细说明)
  • 如果您想绘制 10x10,是的,您将指定坐标 0-10。如果您将第二个坐标视为宽度/高度,那么它会变得更加直观。如果你想要一个 1 像素的宽度,那么你会做 0-1。这背后的原因更复杂(如果您有兴趣,请参阅opengl.org/documentation/specs/version1.1/glspec1.1/node47.html 中的“半开放”讨论)
  • 我发现最容易将这些坐标视为位于像素之间,而不是像素上。 (我认为它实际上也是正确的。)那么 0-10 的事情就很有意义了。
  • 这些解释使“减 1”的东西都有意义。我认为此时我对我的问题的那部分非常清楚,并且所有这些错误都已解决。谢谢!主要问题仍然存在。

标签: iphone opengl-es 2d


【解决方案1】:

[(EAGLView *)self.view setContentScaleFactor:2.0f];

默认情况下,iPhone 窗口会进行缩放以达到其高分辨率模式。这破坏了我的图像质量..

感谢大家的帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 2010-12-23
    • 1970-01-01
    相关资源
    最近更新 更多