SDL程序颜色偏差

3
我目前正在使用纯C方法与SDL(及其官方的额外库,如SDL_image)和OpenGL开发一个非常简单的游戏。但现在我遇到了一些问题,不知道为什么会这样:绘制时颜色全都偏离了。我目前在Mac上运行程序,但是如果我记得正确的话,在Windows上运行时颜色更接近正确,但仍然存在一些奇怪的问题(例如,一个纯白色多边形绘制成黄色)。
目前在我的Mac上,所有加载为png文件的图像颜色都有点偏差,并且一个纯白色的多边形被绘制成深绿色。还有一些图像被绘制成纯白色。如果我记得正确的话,在Windows上,图像被正确绘制,但是之前提到的白色多边形是黄色的。现在我将发布初始化、加载等相关代码。
int main( int argc, char *argv[] ) {
//initializing various OpenGL attributes with SDL utilities
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); //needs for 3D
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); //only needed for systems other than mac osx
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );

    /* Set 640x480 video mode */
    screen=SDL_SetVideoMode(screen_width,screen_height, 8, videoflags );
        if (screen == NULL) {
        fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s\n",
                        screen_width, screen_height,
                        video_info->vfmt->BitsPerPixel, SDL_GetError());
        exit(2);
    }

    glShadeModel( GL_SMOOTH );

    glClearColor( 0.3f, 0.3f, 0.3f, 0 );

    glViewport( 0, 0, (GLsizei)screen->w, (GLsizei)screen->h );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();

    gluOrtho2D( 0.0f, screen->w, 0.0f, screen->h );

    /*glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );*/
    glEnable( GL_ALPHA_TEST );
    glAlphaFunc( GL_GREATER, 0.1f );

// Some basic initialization stuff goes here
// Assume infinite while loop goes here, except when esc is pressed

        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();

        glEnable( GL_TEXTURE_2D );
        draw_gui();

        // Things get drawn here

        glDisable( GL_TEXTURE_2D );

        SDL_GL_SwapBuffers();
        SDL_Delay( 10 );

// End of while loop, clean up, etc.
}

现在我将展示实际将图像加载到内存中的代码:
Sprite *gen_sprite( char *file ) {
    SDL_Surface *buffer = IMG_Load( file );
    if( buffer == NULL ) {
        fprintf( stderr, "Could not load image '%s'\n for reason: %s\n",
                file, IMG_GetError() );
        exit( 3 );
    } 
    return gen_sprite_from( buffer );
}

Sprite *gen_sprite_from( SDL_Surface *buffer ) {
    Sprite *sprite;
    GLuint texture;

    if( buffer == NULL ) {
        fprintf( stderr, "NULL surface passed to gen_sprite_from." );
        exit( 3 );
    }    
    texture = gen_Gl_texture_from( buffer );
    if( ( sprite = malloc( sizeof( Sprite ) ) ) == NULL ) {
        fprintf( stderr, "Malloc failed to allocate space for a Sprite.\n" );
        exit( 1 );
    }
    if( ( sprite->tex = malloc( sizeof( GLuint ) ) ) == NULL ) {
        fprintf( stderr, "Malloc failed to allocate space for a GLuint.\n" );
        exit( 1 );
    }
    sprite->tex[ 0 ] = texture;
    sprite->original = buffer;
    sprite->is_animation = 0;
    sprite->cur_frame = 0;
    sprite->cur_time = 0;
    sprite->num_frames = 1;
    sprite->frame_time = NULL;

    return sprite;
}

Uint32 gen_Gl_texture_from( SDL_Surface *buffer ) {
    GLuint texture;
    SDL_Surface *temp;

    glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
    glGenTextures( 1, &texture );
    temp = SDL_CreateRGBSurface( SDL_SWSURFACE, buffer->w, buffer->h, 32,

        0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 );
    SDL_SetAlpha( buffer, 0, SDL_ALPHA_OPAQUE );
    SDL_BlitSurface( buffer, NULL, temp, NULL );
    glBindTexture( GL_TEXTURE_2D, texture );
    //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
    gluBuild2DMipmaps( GL_TEXTURE_2D, 4,
                      temp->w, temp->h,
                      GL_RGBA, GL_UNSIGNED_BYTE,
                      temp->pixels );
    SDL_FreeSurface( temp );

    // This just creates white blocks instead of actually loading textures
    //glPixelStorei( GL_UNPACK_ALIGNMENT, buffer->format->BytesPerPixel );
    //glGenTextures( 1, &texture );
    //glBindTexture( GL_TEXTURE_2D, texture );
    //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    //glTexImage2D( GL_TEXTURE_2D, 0, mask_order, buffer->w, buffer->h, 0,
    //  mask_order, GL_UNSIGNED_BYTE, buffer->pixels );

    return texture;
}

现在,我相信所有与颜色偏差有关的代码都已经发布。绘图代码非常简单,只需要进行平移或旋转等操作,绑定纹理,然后使用纹理坐标和顶点进行简单的开始/结束块。如果有人能告诉我为什么颜色会偏差,并让我知道一种在跨平台方式下确保颜色始终正确的好方法(我计划在所有平台上构建,这也是我使用SDL的原因之一),我将不胜感激。


screen=SDL_SetVideoMode(screen_width,screen_height, 8, videoflags ); 这会设置8位颜色...你似乎想要32位,是吗? - Orka
将其更改为32(或将0用于让SDL使用当前屏幕的属性)似乎没有任何不同。绘制的多边形仍然是深绿色,而应该是白色的。 - Freezerburn
尝试使用temp = SDL_CreateRGBSurface( SDL_SWSURFACE, buffer->w, buffer->h, buffer->format->BitsPerPixel, buffer->format->Rmask, buffer->format->Gmask, buffer->format->Bmask, buffer->format->Amask);代替temp = SDL_CreateRGBSurface( SDL_SWSURFACE, buffer->w, buffer->h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 );。如果您没有将缓冲区设置为启用alpha通道的表面,则可能会在初始alpha渲染时出现问题。 - Orka
我非常特别地使用了设置的值,以便我创建的最终表面采用GL_RGBA格式,据说这种格式对于OpenGL实际渲染更快。它还确保所有表面都是标准化的。我相信缓冲区是使用alpha渲染创建的,并且调用SDL_SetAlpha确保alpha通道正确传输到新表面。 - Freezerburn
glClearColor( 0.3f, 0.3f, 0.3f, 0 ); is asking for a grey of 0.3f, fair enough, but that 0 at the end is asking for one that is completely transparent. You probably want one that is opaque... glClearColor( 0.3f, 0.3f, 0.3f, 1.0f ); - burito
1个回答

4
你的频道混乱了。
问题在以下行中显而易见:
temp = SDL_CreateRGBSurface( SDL_SWSURFACE, buffer->w, buffer->h, 32,
    0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 );

并且:

gluBuild2DMipmaps( GL_TEXTURE_2D, 4,
                  temp->w, temp->h,
                  GL_RGBA, GL_UNSIGNED_BYTE,
                  temp->pixels );

您正在SDL中指定由32位单位组成的32位纹理,并将OpenGL纹理指定为4个8位分量。这取决于您的体系结构端序,可能会给您带来正确或不正确的结果。解决方案是按照以下方式将OpenGL纹理指定为 GL_UNSIGNED_INT 而不是 GL_UNSIGNED_BYTE:

gluBuild2DMipmaps( GL_TEXTURE_2D, 4,
                  temp->w, temp->h,
                  GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,
                  temp->pixels );

也许使用GL_UNSIGNED_INT_8_8_8_8_REV是正确的,但我懒得去找出哪一个与你的SDL纹理规范相匹配。或者你可以更改SDL纹理规范。一种类似的格式(实际上是GL_BGRA / GL_UNSIGNED_INT_8_8_8_8_REV,我认为,可能我错了)完全匹配一个常见的内部格式,并且上传到图形卡会稍微快一些,但除非你上传了很多纹理,否则你不会注意到这点。
作为一种风格上的注意事项,通常最好更精确地指定内部格式,像这样:
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, ....

GL_RGBA/GL_UNSIGNED_INT_8_8_8_8_REV主要是修复问题。然而,我试图绘制的白色多边形仍然是深绿色,并且仍有一些纹理在Windows上正常,但在这里却变成了白色。现在我认为白色纹理可能是另一个问题,我会看看能否找到原因。 - Freezerburn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接