使用 SDL 加载 OpenGL 纹理

Posted

技术标签:

【中文标题】使用 SDL 加载 OpenGL 纹理【英文标题】:OpenGL texture loading with SDL 【发布时间】:2012-04-26 08:31:00 【问题描述】:

我已经开始使用 NeHe 教程学习 OpenGL。这是第 6 课的代码。它应该加载一个 bmp 图像并将其用作我正在绘制的立方体的纹理。但是它不能正常工作立方体完全保持白色。加载图像的功能是“loadGLTextures”。有人可以帮忙吗? 我的图像位深度是 24。我正在使用 Visual Studio 2010。

#include <Windows.h>
#include <stdio.h>
#include <gl\GL.h>
#include <gl\GLU.h>
#include <SDL\SDL.h>


#pragma comment(lib , "SDL.lib")
#pragma comment(lib , "SDLmain.lib")
#pragma comment(lib , "OPENGL32.lib")
#pragma comment(lib , "glu32.lib")


//height , width and bit depth
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define SCREEN_BPP 16

//SDL surface
SDL_Surface* surface;

//Texture storage.
GLuint texture[1];

//Quit func.
void Quit(int returnCode)

    SDL_Quit();
    exit(returnCode);


//This function will load a bitmap image.
bool loadGLTextures(void)

    SDL_Surface* textureImage;
    textureImage = SDL_LoadBMP("123.bmp");
    if(!textureImage)
    
        fprintf(stderr , "Couldn't load %s.\n" , "123.bmp");
        return false;
    

    else
    
        //Create the texture.
        glGenTextures(1 , &texture[0]);

        //Typical texture generation using data from the bitmap.
        glBindTexture(GL_TEXTURE_2D , texture[0]);

        //Generate the texture.
        glTexImage2D(GL_TEXTURE_2D , 0 , 3 , textureImage->w , 
            textureImage->h , 0 , GL_RGB , GL_UNSIGNED_BYTE , 
            textureImage->pixels);

        //Linear filtering.
        glTexParameteri(GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR);

        //Free up the memory.
        if(textureImage)
            SDL_FreeSurface(textureImage);

        return true;
    



//All of the drawing goes throw this.
int drawGLScene(void)

    static float xrot = 0 , yrot = 0 , zrot = 0;
    //Clear screen and depth buffer.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glTranslatef(0.0f , 0.0f , -5.0f);
    glRotatef(xrot , 1.0f , 0.0f , 0.0f);
    glRotatef(yrot , 0.0f , 1.0f , 0.0f);
    glRotatef(zrot , 0.0f , 0.0f  ,1.0f);

    //Select the texture.
    glBindTexture(GL_TEXTURE_2D , texture[0]);

    glBegin(GL_QUADS);
    //Front:
    //Bottom left of the texture and quad.
    glTexCoord2f(0.0f , 0.0f); glVertex3f(-1.0f , -1.0f , 1.0f);
    //Bottom right fo the texture and quad.
    glTexCoord2f(1.0f , 0.0f); glVertex3f(1.0f , -1.0f , 1.0f);
    //Top right of the texture and quad.
    glTexCoord2f(1.0f , 1.0f); glVertex3f(1.0f , 1.0f , 1.0f);
    //Top left of the texture and quad.
    glTexCoord2f(0.0f , 1.0f); glVertex3f(-1.0f , 1.0f , 1.0f);

    //Back:
    //Bottom left of the texture and quad.
    glTexCoord2f(0.0f , 0.0f); glVertex3f(1.0f , -1.0f , -1.0f);
    //Bottom right of the texture and quad.
    glTexCoord2f(1.0f , 0.0f); glVertex3f(-1.0f , -1.0f , -1.0f);
    //Top right of the texture and the quad.
    glTexCoord2f(1.0f , 1.0f); glVertex3f(-1.0f , 1.0f , -1.0f);
    //Top left of the texture and the quad.
    glTexCoord2f(0.0f , 1.0f); glVertex3f(1.0f , 1.0f , -1.0f);

    //Top:
    //Top right of the texture and quad.
    glTexCoord2f(1.0f , 1.0f); glVertex3f(1.0f , 1.0f , -1.0f);
    //Top left of the texture and quad.
    glTexCoord2f(0.0f , 1.0f); glVertex3f(-1.0f , 1.0f , -1.0f);
    //Bottom left of the texture and quad.
    glTexCoord2f(0.0f , 0.0f); glVertex3f(-1.0f , 1.0f , 1.0f);
    //Bottom right of the texture and quad.
    glTexCoord2f(0.0f , 1.0f); glVertex3f(1.0f , 1.0f , 1.0f);

    //Bottom:
    //Top left of the texture and quad.
    glTexCoord2f(0.0f , 1.0f); glVertex3f(-1.0f , -1.0f , 1.0f);
    //Bottom left of the texture and quad.
    glTexCoord2f(0.0f , 0.0f); glVertex3f(-1.0f , -1.0f , -1.0f);
    //Bottom right of the texture and quad.
    glTexCoord2f(1.0f , 0.0f); glVertex3f(1.0f , -1.0f , -1.0f);
    //Top right of the texture and quad.
    glTexCoord2f(1.0f , 1.0f); glVertex3f(1.0f , -1.0f , 1.0f);

    //Right:
    //Bottom right of the texture and quad.
    glTexCoord2f(1.0f , 0.0f); glVertex3f(1.0f , -1.0f , -1.0f);
    //Top right of the texture and quad.
    glTexCoord2f(1.0f , 1.0f); glVertex3f(1.0f , 1.0f , -1.0f);
    //Top left of the texture and quad.
    glTexCoord2f(0.0f , 1.0f); glVertex3f(1.0f , 1.0f , 1.0f);
    //Bottom left of the texture and quad.
    glTexCoord2f(0.0f , 0.0f); glVertex3f(1.0f , -1.0f , 1.0f);

    //Left:
    //Bottom left of the texture and quad.
    glTexCoord2f(0.0f , 0.0f); glVertex3f(-1.0f , -1.0f , -1.0f);
    //Bottom right of the texture and quad.
    glTexCoord2f(1.0f , 0.0f); glVertex3f(-1.0f , -1.0f , 1.0f);
    //Top right of the texture and quad.
    glTexCoord2f(1.0f , 1.0f); glVertex3f(-1.0f , 1.0f , 1.0f);
    //Top left of the texture and quad.
    glTexCoord2f(0.0f , 1.0f); glVertex3f(-1.0f , 1.0f , -1.0f);
    glEnd();

    SDL_GL_SwapBuffers();

    xrot += 0.1;
    yrot += 0.1;
    zrot += 0.1;

    return true;



//This function will reset our viewport after a windows resize.
int resizeWindow(int width , int height)

    //Height / width ration.
    float ratio;

    //Protect against a division by zero.
    if(height == 0)
        height = 1;

    ratio = width / height;

    //Setup viewport
    glViewport(0 , 0 , width , height);

    //Change to the projection matrix and reset it.
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    //set perspective.
    gluPerspective(45.0f , ratio , 0.1f , 100.0f);

    //Change to model view matrix and reset it.
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    return true;


//Toggle fullScreen.
void toggleFullscreen(SDL_Surface* screen)

    int videoFlags = screen->flags;
    (videoFlags & SDL_FULLSCREEN) == SDL_FULLSCREEN ? videoFlags ^= SDL_FULLSCREEN : videoFlags |= SDL_FULLSCREEN;//NICE!!
    screen = SDL_SetVideoMode(SCREEN_WIDTH , SCREEN_HEIGHT , SCREEN_BPP , videoFlags);
    resizeWindow(surface->w , surface->h);
    drawGLScene();


//OpenGL initialization.
int initGL(void)

    if(!loadGLTextures())
        return false;

    glShadeModel(GL_SMOOTH);
    glEnable(GL_TEXTURE_2D); //Enable texture mapping.
    glClearColor(0.0f , 0.0f , 0.0f , 0.5f);
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    //Nice perspective.
    glHint(GL_PERSPECTIVE_CORRECTION_HINT , GL_NICEST);
    return true;


//This func will handle any key inputs.
void handleKeyPress(SDL_keysym* keysym)

    switch(keysym->sym)
    
    case SDLK_ESCAPE:
            Quit(0);
            break;
    case SDLK_F1:
        toggleFullscreen(surface);
        break;
    case SDLK_r:
        drawGLScene();
        break;
    default:
        break;
    
    return;



int main(int argc , char* argv[])

    //Flags to pass to SDL_SetVideoMode : awsome!! ints can be compiled.
    int videoFlags;
    //Event
    SDL_Event event;
    //Holds information about display.
    const SDL_VideoInfo* videoInfo;
    //Is window active?
    bool isActive = true;

    //SDL initialization.
    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    
        fprintf(stderr , "SDL video initialization failed : %s\n" , SDL_GetError());
        Quit(1);
    

    //Fetch the video info.
    videoInfo = SDL_GetVideoInfo();

    if(!videoInfo)
    
        fprintf(stderr , "Video query failed : %s\n" , SDL_GetError());
        Quit(1);
    

    //Add flags to pass to SDL_SetVideoMode.
    videoFlags = SDL_OPENGL;              //Enable OpenGL in SDL.
    videoFlags |= SDL_GL_DOUBLEBUFFER;    //Enable double buffering.
    videoFlags |= SDL_HWPALETTE;          //Store the palette in hardware.
    videoFlags |= SDL_RESIZABLE;          //Enable window resizing.

    //This checks to see if surfaces can be stored in hardware.
    videoInfo->hw_available ? videoFlags |= SDL_HWSURFACE : SDL_SWSURFACE;

    //This checks if harware blits can be done.
    if(videoInfo->blit_hw)
        videoFlags |= SDL_HWACCEL;

    //Set OpenGL double buffering.
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER , 1);

    surface = SDL_SetVideoMode(SCREEN_WIDTH , SCREEN_HEIGHT , 16 , videoFlags);

    //verify the surface.
    if(!surface)
    
        fprintf(stderr , "Video mode set failed : %s\n" , SDL_GetError());
        Quit(1);
    

    SDL_WM_SetCaption("OpenGL-Sample" , 0);

    //initialize OpenGL
    if(initGL() == false)
    
        fprintf(stderr , "Could not initialize OpenGL.\n");
        Quit(1);
    

    //Main loop
    while(1)
    
        //Handle the events in the queue.
        if(SDL_PollEvent(&event))
        
            switch(event.type)
            
            case SDL_ACTIVEEVENT:
                if(event.active.gain == 0)
                    isActive = false;
                else
                    isActive = true;
                break;
            case SDL_VIDEORESIZE:
                //Handle resize event.
                surface = SDL_SetVideoMode(event.resize.w , event.resize.h , SCREEN_BPP , videoFlags);
                if(!surface)
                
                    fprintf(stderr , "Could not get a surface after resize : %s\n" , SDL_GetError());
                    Quit(1);
                
                resizeWindow(event.resize.w , event.resize.h);
                break;
            case SDL_KEYDOWN:
                handleKeyPress(&event.key.keysym);
                break;
            case SDL_QUIT:
                Quit(0);
            default:
                break;
            
        
        if(isActive)
            drawGLScene();
    


这是我正在尝试加载的image。

【问题讨论】:

使用glGetError在GL调用后检查无效条件。 我找到了这个。但它不起作用。 content.gpwiki.org/index.php/… 【参考方案1】:

我使用 SOIL 库,http://www.lonesock.net/soil.html

这是一个非常容易使用的 OpenGL 纹理加载器库,你不必 担心图像格式或自己制作任何加载代码,它会为你做这一切。

加载纹理就这么简单:

int textureID = SOIL_load_OGL_texture("img.png", SOIL_LOAD_AUTO, 
    SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);
if(textureID == 0)  cout << "Failed to load texture!" << endl ;

【讨论】:

【参考方案2】:

我能够通过更改让您的程序生成纹理(尽管它是镜像的):

        textureImage->h , 0 , GL_RGB , GL_UNSIGNED_BYTE , 

到:

        textureImage->h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 

不幸的是,弄错该字段不会产生任何错误。如果你试图告诉 GL 图像数据比实际数据大,可能会导致程序崩溃,但传递 GL_RGB 而不是 GL_RGBA 的效果是说它比实际数据小。

请记住,SDL_LoadBMP() 不会尝试转换图像数据,因此您必须确保 BMP 文件的格式符合程序的预期。您可能需要使用 GL_RGBA 或 GL_RGB。

【讨论】:

【参考方案3】:

一切都取决于您的图片格式。正如迈克尔所说,SDL_LoadBMP 不会转换图像数据。所以你不能确定你应该通过哪个标志。 我建议您使用SDL_Image 库。它将所有图像格式转换为一种特定格式。然后您可以使用(例如)GL_RGBA 标志并确保一切正常!

【讨论】:

SDL_image 也无法正常工作。如果您想查看,我已在问题末尾添加了图片。

以上是关于使用 SDL 加载 OpenGL 纹理的主要内容,如果未能解决你的问题,请参考以下文章

SDL OpenGL 纹理显示问题

SDL 3d OpenGL:立方体上的纹理产生空白屏幕

c++:如何使用 sdl 将位图加载到 opengl 中的多维数据集中?

使用现代 OpenGL 渲染纹理

神秘的 OpenGL/SDL 内存增长

使用 openGL 在 SDL2 中全屏显示窗口