【问题标题】:openGL image quality (blured)openGL 图像质量(模糊)
【发布时间】:2012-08-04 11:54:27
【问题描述】:

我使用 openGL 创建幻灯片应用程序。不幸的是,与 gnome 图像查看器相比,使用 openGL 渲染的图像看起来模糊

这是两张截图

(opengl)http://tinyurl.com/dxmnzpc

(图像查看器)http://tinyurl.com/8hshv2a

这是基本图像: http://tinyurl.com/97ho4rp

图像具有我屏幕的原始尺寸。 (2560x1440)

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <unistd.h>

GLuint text = 0;

GLuint load_texture(const char* file) {
        SDL_Surface* surface = IMG_Load(file);

        GLuint texture;
        glPixelStorei(GL_UNPACK_ALIGNMENT,4);
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        SDL_PixelFormat *format = surface->format;
        printf("%d %d \n",surface->w,surface->h);
        if (format->Amask) {
                gluBuild2DMipmaps(GL_TEXTURE_2D, 4,surface->w, surface->h, GL_RGBA,GL_UNSIGNED_BYTE, surface->pixels);
        } else {
                gluBuild2DMipmaps(GL_TEXTURE_2D, 3,surface->w, surface->h, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
        }
        SDL_FreeSurface(surface);

        return texture;
}

void display(void) {
        GLdouble offset_x = -1;
        GLdouble offset_y = -1;

        int p_viewport[4];
        glGetIntegerv(GL_VIEWPORT, p_viewport); 

        GLfloat gl_width = p_viewport[2];//width(); // GL context size
        GLfloat gl_height = p_viewport[3];//height();

        glClearColor (0.0,2.0,0.0,1.0);
        glClear (GL_COLOR_BUFFER_BIT);

        glLoadIdentity();
        glEnable( GL_TEXTURE_2D );
        glTranslatef(0,0,0);

        glBindTexture( GL_TEXTURE_2D, text);

        gl_width=2; gl_height=2;

        glBegin(GL_QUADS);
        glTexCoord2f(0, 1); //4
        glVertex2f(offset_x, offset_y);

        glTexCoord2f(1, 1); //3
        glVertex2f(offset_x + gl_width, offset_y);

        glTexCoord2f(1, 0); // 2
        glVertex2f(offset_x + gl_width, offset_y + gl_height);

        glTexCoord2f(0, 0); // 1
        glVertex2f(offset_x, offset_y + gl_height);
        glEnd();

        glutSwapBuffers();
}


int main(int argc, char **argv) {
        glutInit(&argc,argv);
        glutInitDisplayMode (GLUT_DOUBLE);

        glutGameModeString("2560x1440:24");
        glutEnterGameMode();       
        text = load_texture("/tmp/raspberry/out.jpg");

        glutDisplayFunc(display);
        glutMainLoop();
}

更新的尝试

void display(void)                                                                                                         
{                                                                                                                          
        GLdouble texture_x = 0;                                                                                            
        GLdouble texture_y = 0;                                                                                            
        GLdouble texture_width = 0;                                                                                        
        GLdouble texture_height = 0;                                                                                       

        glViewport(0,0,width,height);                                                                                      

        glClearColor (0.0,2.0,0.0,1.0);                                                                                    
        glClear (GL_COLOR_BUFFER_BIT);                                                                                     
        glColor3f(1.0, 1.0, 1.0);                                                                                          

        glMatrixMode(GL_PROJECTION);                                                                                       
        glLoadIdentity();                                                                                                  
        glOrtho(0, width, 0, height, -1, 1);                                                                               

        //Do pixel calculatons                                                                                             
        texture_x = ((2.0*1-1) / (2*width));                                                                               
        texture_y = ((2.0*1-1) / (2*height));                                                                              
        texture_width=((2.0*width-1)/(2*width));                                                                           
        texture_height=((2.0*height-1)/(2*height));                                                                        

        glMatrixMode(GL_MODELVIEW);                                                                                        
        glLoadIdentity();                                                                                                  
        glTranslatef(0,0,0);                                                                                               

        glEnable( GL_TEXTURE_2D );                                                                                         
        glBindTexture( GL_TEXTURE_2D, text);                                                                               

        glBegin(GL_QUADS);                                                                                                 
        glTexCoord2f(texture_x, texture_height); //4                                                                       
        glVertex2f(0, 0);                                                                                                  

        glTexCoord2f(texture_width, texture_height); //3                                                                   
        glVertex2f(width, 0);                                                                                              

        glTexCoord2f(texture_width, texture_y); // 2                                                                       
        glVertex2f(width,height);                                                                                          

        glTexCoord2f(texture_y, texture_y); // 1                                                                           
        glVertex2f(0,height);                                                                                              
        glEnd();                                                                                                           

        glutSwapBuffers();                                                                                                 
}     

【问题讨论】:

    标签: image opengl sdl


    【解决方案1】:

    您遇到的是 栅栏问题 的一种变体,它源于 OpenGL 处理纹理坐标的方式。 OpenGL 不处理纹理的像素(纹素),而是使用图像数据作为插值的支持,这实际上覆盖了比图像像素更广的范围。所以纹理坐标 0 和 1 并没有击中最左/最下和最右/最上的像素,而是更进一步,事实上。

    假设纹理是 8 像素宽:

     | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
     ^   ^   ^   ^   ^   ^   ^   ^   ^
    0.0  |   |   |   |   |   |   |  1.0
     |   |   |   |   |   |   |   |   |
    0/8 1/8 2/8 3/8 4/8 5/8 6/8 7/8 8/8
    

    数字表示纹理的像素,条形表示纹理的边缘,并且在最近过滤像素之间的边界的情况下。但是,您想击中像素的中心。所以你对纹理坐标感兴趣

    (0/8 + 1/8)/2 = 1 / (2 * 8)

    (1/8 + 2/8)/2 = 3 / (2 * 8)

    ...

    (7/8 + 8/8)/2 = 15 / (2 * 8)

    或者更一般地说,对于 N 宽纹理中的像素 i,正确的纹理坐标是

    (2i + 1)/(2N)

    但是,如果您想将纹理与屏幕像素完美对齐,请记住您指定的坐标不是四边形的像素,而是边缘,根据投影,边缘可能与屏幕像素边缘对齐,而不是中心,因此可能需要其他纹理坐标。

    请注意,如果您遵循这一点,无论您的过滤模式和 mipmap 是什么,您的图像都将始终看起来清晰明快,因为插值恰好符合您的采样支持,即您的输入图像。切换到另一种过滤模式,如 GL_NEAREST 乍一看可能看起来不错,但实际上并不正确,因为它会给您的样本起别名。 所以不要这样做。


    您的代码也几乎没有其他问题,但它们并不是一个大问题。首先,您选择了一种相当神秘的方式来查看视口尺寸。您(可能没有进一步考虑)发现默认 OpenGL 视口是创建上下文的窗口的大小。您正在使用 SDL,它具有副作用,只要您坚持使用 SDL-1,这种方法就不会对您不利。但是切换到任何其他框架,它可能会通过代理 drawable 创建上下文,而您遇到了问题。

    规范的方法通常是从窗口系统(在您的情况下为 SDL)检索窗口大小,然后在显示函数中的第一个操作中设置视口。

    另一个问题是你使用gluBuildMipmaps,因为a)你不想使用mipmaps和b)因为OpenGL-2你可以上传任意大小的纹理图像(即你不限于2的幂对于尺寸),这完全消除了对 gluBuildMipmaps 的需要。所以不要使用它。直接使用glTexImage2D,切换到non-mipmapping过滤模式即可。

    由于问题更新而更新

    您计算纹理坐标的方式仍然看起来不正确。看起来你从 1 开始计数。纹理像素的基础索引为 0,所以……

    这就是我的做法:

    假设投影映射到视口

        glMatrixMode(GL_PROJECTION);                                                                                       
        glLoadIdentity();                                                                                                  
        glOrtho(0, win_width, 0, win_height, -1, 1);   
    
        glViewport(0, 0, win_width, win_height);
    

    我们计算纹理坐标为

        //Do pixel calculatons      
        glBindTexture( GL_TEXTURE_2D, text);                                                                                           
        GLint tex_width, tex_height;
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &tex_width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_height);
    
        texture_s1 = 1. / (2*width);  // (2*0-1
        texture_t1 = 1. / (2*height);                        
        texture_s2 = (2.*(tex_width -1) + 1) / (2*width);
        texture_t2 = (2.*(tex_height-1) + 1) / (2*height);
    

    请注意,tex_width 和 tex_height 给出了每个方向上的像素数量,但坐标是基于 0 的,因此您必须从它们中减去 1 以进行纹理坐标映射。因此,我们还在 s1、t1 坐标的分子中使用常数 1。

    根据您选择的投影,其余部分看起来还不错

        glEnable( GL_TEXTURE_2D );                                                                                         
    
        glBegin(GL_QUADS);                                                                                                 
        glTexCoord2f(s1, t1); //4                                                                       
        glVertex2f(0, 0);                                                                                                  
    
        glTexCoord2f(s2, t1); //3                                                                   
        glVertex2f(tex_width, 0);                                                                                              
    
        glTexCoord2f(s2, t2); // 2                                                                       
        glVertex2f(tex_width,tex_height);                                                                                          
    
        glTexCoord2f(s1, t2); // 1                                                                           
        glVertex2f(0,tex_height);                                                                                              
        glEnd();
    

    【讨论】:

    • hmm ok: 1 改变 gl_width=1+((2.0*2560-1)/(2*2560));和高度和您的公式的偏移量。我也改成了 GL_LINEAR glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);但直接与 gl_width = 2 相比; offset_x = -1 你的复数计算结果看起来更模糊(但质量非常接近)
    • @aatdark:您是否还考虑了您的顶点位置是否正确?您没有设置投影,并且您的 gl_width 和 gl_height 变量在您的代码中并没有真正意义。我建议将 GL_PPROJECTION 矩阵设置为 glOrtho(0, width, 0, height, -1, 1),并使用 GL_MODELVIEW 应用 glTranslatef 的偏移量。
    • 好的,我在上面发布了更新的源代码。再次模糊了一点。过滤是 GL_LINEAR。我想显示已经与屏幕分辨率完全相同的图像。
    • 我使用了您的代码,但 s1=0,t1=0,s2=1,t2=1 看起来仍然更好。也许 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);是问题所在。无论如何,谢谢你的努力;)至少我摆脱了 NEAREST ..
    【解决方案2】:

    我不确定这是否真的是问题所在,但我认为您在这里不需要/想要 mipmap。您是否尝试过使用glTexImage2D 而不是gluBuild2DMipmaps 结合最近邻过滤(glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN/MAG_FILTER, GL_NEAREST);)?

    【讨论】:

    • 将过滤切换到 GL_NEAREST 并不能解决 OP 的问题。它可能看起来有效,但实际上并没有。请参阅我的答案以获得解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 2015-06-10
    相关资源
    最近更新 更多