SDL2 with OpenGL

本文介绍如何使用SDL2和OpenGL进行跨平台游戏开发。通过Valve的支持,SDL2成为了一个重要的游戏开发库。文章详细讲解了如何设置OpenGL环境,并创建一个简单的窗口应用。

http://www.zhieng.com/sdl2-with-opengl/

Since the day Valve purchased SDL and revamped most of its source code, SDL 2 has been seen by many as the game changer that will reshape the linux (and Mac OSX) gaming scene by allowing developers to easily port their games to these platforms.

Although there are some other APIs that are equally portable, but none of them are supported by big company with strong financial muscle and development experience like Valve. The major advantage of SDL being used by a company that has had countless experience developing AAA titles is that they can help avoiding ideas that sound good only on paper but not working quite well when applied to real world problems. This is why I think SDL 2 is a library worth studying for long term benefit.

In this tutorial, I’m going to directly use the OpenGL for rendering, instead of using SDL’s own renderer, because OpenGL works better in term of performance and flexibility. You can develop both 2D/3D games with OpenGL but only 2D if you use SDL renderer.

So let’s get it started by opening your favorite C/C++ IDE, which in my case, the Code::Blocks. You have to link your project to several libraries such as OpenGL, GLEW and SDL2. GLEW is basically a library that makes your life easier for connecting to OpenGL extensions. Different IDE has different approach in linking to external libraries, so I will not be explaining it here. Check out other tutorials that are related to your choice of IDE.

Let’s start writing some code. First of all, you need to link all the relevant header files to your main source file, using the #include macro. Notice we used #define GLEW_STATIC before #include in-order to build GLEW statically into your project. Without the macro, you need to provide the dynamic library file for the compiler.

// OpenGL headers
#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/glu.h>
#include <GL/gl.h>

// SDL headers
#include <SDL_main.h>
#include <SDL.h>
#include <SDL_opengl.h>

Next, define all the necessary variables, in this case, a boolean which determines whether the game is still running or not, as well as other pointers/references that are linked to the SDL window, OpenGL context, and SDL event.

bool quit;

SDL_Window* window;
SDL_GLContext glContext;
SDL_Event sdlEvent;

After that, let’s move on to the main() function. Here we define the quit variable as false, so that later on we can use it for the game loop and keep the game running without closing. We also set the OpenGL’s context to 3.1 core profile. Do notice that in version 3.1 above, the fixed-function pipeline is completely deprecated, so you need to be careful when picking the right context for your OpenGL renderer.

int main(int argc, char *argv[])
{
    quit = false;

    //Use OpenGL 3.1 core
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
}

Next, we call SDL_Init(SDL_INIT_VIDEO) to initiate SDL. If the result returned by the function is -1, it means something is going wrong and you should stop immediately. Otherwise, you are good to go and start creating the rendering window using SDL_CreateWindow(). There are several inputs for this functions, such as:

1. Title
2. Horizontal position
3. Vertical position
4. Width
5. Height
6. Flags

We need to specifically set the type of window as OpenGL rendering window using the SDL_WINDOW_OPENGL flag. Otherwise, SDL will not be able to use OpenGL to do the rendering.

After we have created a window, use it to create the OpenGL context by calling SDL_GL_CreateContext(window). Call glewInit() to automatically load all the relevant extensions based on the OpenGL context you have just set.

// Initialize video subsystem
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
    // Display error message
    printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
    return false;
}
else
{
    // Create window
    window = SDL_CreateWindow("Hello World!", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
    if( window == NULL )
    {
        // Display error message
        printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
        return false;
    }
    else
    {
        // Create OpenGL context
        glContext = SDL_GL_CreateContext(window);

        if( glContext == NULL )
        {
            // Display error message
            printf( "OpenGL context could not be created! SDL Error: %s\n", SDL_GetError() );
            return false;
        }
        else
        {
            // Initialize glew
            glewInit();
        }
    }
}

You have now created an empty window that doesn’t render anything and will be closed once it’s being launched. This happen because we have not yet created the game loop for it. A game loop is basically a while loop that keeps on running repeatedly until the user closes the application. In gaming term, each loop is called a “frame” or a “tick”. Graphics will be rendered and displayed on the screen for several times a second, hence the term for measuring the rate of rendering is called “frame-per-second”.

To create the game loop, we will be using the quit variable that we have defined previously. If the variable quit is true, the window will never be closed and keep the rendering going. Within the game loop, we detect the keyboard event and check if player has pressed the Escape button. If the Escape button is pressed, set quit as true and the application will then be closed.

We also set the background color as cornflower blue, and then ask OpenGL to start rendering the scene by swapping the render buffer. We don’t have anything to be rendered for now so you will just see a blue color background.

// Game loop
while (!quit)
{
    while(SDL_PollEvent(&sdlEvent) != 0)
    {
        // Esc button is pressed
        if(sdlEvent.type == SDL_QUIT)
        {
            quit = true;
        }
    }

    // Set background color as cornflower blue
    glClearColor(0.39f, 0.58f, 0.93f, 1.f);
    // Clear color buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Update window with OpenGL rendering
    SDL_GL_SwapWindow(window);
}

Lastly, we need to clean everything up if the variable quit is set to true and breaks the game loop. It’s important to quit the application neatly! Destroy the rendering window properly by calling SDL_DestroyWindow(window), then clear the window pointer from the memory by setting the pointer to NULL, and lastly call SDL_Quit() to make sure SDL quits properly.

//Destroy window
SDL_DestroyWindow(window);
window = NULL;

//Quit SDL subsystems
SDL_Quit();

return 0;

For those who are lazy, this is the full source code:

// OpenGL headers
#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/glu.h>
#include <GL/gl.h>

// SDL headers
#include <SDL_main.h>
#include <SDL.h>
#include <SDL_opengl.h>

bool quit;

SDL_Window* window;
SDL_GLContext glContext;
SDL_Event sdlEvent;

int main(int argc, char *argv[])
{
    quit = false;

    //Use OpenGL 3.1 core
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    // Initialize video subsystem
    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        // Display error message
        printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
        return false;
    }
    else
    {
        // Create window
        window = SDL_CreateWindow("Hello World!", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
        if( window == NULL )
        {
            // Display error message
            printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
            return false;
        }
        else
        {
            // Create OpenGL context
            glContext = SDL_GL_CreateContext(window);

            if( glContext == NULL )
            {
                // Display error message
                printf( "OpenGL context could not be created! SDL Error: %s\n", SDL_GetError() );
                return false;
            }
            else
            {
                // Initialize glew
                glewInit();
            }
        }
    }

    // Game loop
    while (!quit)
    {
        while(SDL_PollEvent(&sdlEvent) != 0)
        {
            // Esc button is pressed
            if(sdlEvent.type == SDL_QUIT)
            {
                quit = true;
            }
        }

        // Set background color as cornflower blue
        glClearColor(0.39f, 0.58f, 0.93f, 1.f);
        // Clear color buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // Update window with OpenGL rendering
        SDL_GL_SwapWindow(window);
    }

    //Destroy window
    SDL_DestroyWindow(window);
    window = NULL;

    //Quit SDL subsystems
    SDL_Quit();

    return 0;
}

06-30
在Qt中集成SDL2进行多媒体开发是一种常见的需求,特别是在需要高性能图形渲染、音频播放或游戏开发的场景下。以下是一个详细的开发指南: ### 环境准备 1. **安装Qt开发环境** 从[Qt官网](https://www.qt.io/)下载并安装适合你操作系统的Qt版本,推荐使用较新的版本(如Qt 5.14.2 或 Qt 6.x),并选择与你的编译器兼容的版本(如 MSVC 2019 或 MinGW)。[^2] 2. **安装SDL2库** 从[SDL官网](https://www.libsdl.org/download-2.0.php)下载适用于你平台的SDL2开发库。对于Windows用户,建议下载预编译的开发包,并确保将头文件库文件分别复制到项目的`include``lib`目录中。[^3] ### 配置Qt项目以支持SDL2 1. **修改`.pro`文件** 在Qt项目的`.pro`文件中添加SDL2的配置信息。例如: ```qmake QT += core gui widgets TARGET = SDL2WithQt TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp \ sdlwidget.cpp HEADERS += mainwindow.h \ sdlwidget.h # SDL2设置 win32 { LIBS += -L$$PWD/../../SDL2-2.0.22/x86_64-w64-mingw32/lib/ -lSDL2main -lSDL2 INCLUDEPATH += $$PWD/../../SDL2-2.0.22/x86_64-w64-mingw32/include/SDL2 DEFINES += _WINDOWS } ``` 2. **创建自定义的SDL显示组件** 创建一个继承自`QWidget`的类,并在其内部初始化SDL2。例如: ```cpp #include <SDL.h> #include <QWidget> class SDLWidget : public QWidget { Q_OBJECT public: explicit SDLWidget(QWidget *parent = nullptr); ~SDLWidget(); void initSDL(); void renderFrame(); private: SDL_Window* sdlWindow; SDL_Renderer* sdlRenderer; SDL_Texture* sdlTexture; }; ``` 在实现文件中,可以使用`SDL_CreateWindowFrom`来将SDL窗口嵌入到Qt的`QWidget`中。 3. **主窗口集成SDLWidget** 在主窗口中使用该自定义组件,并将其作为视频输出区域: ```cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { sdlWidget = new SDLWidget(this); setCentralWidget(sdlWidget); sdlWidget->initSDL(); } ``` ### SDL2与Qt事件循环的协调 由于QtSDL2各自有自己的事件循环机制,因此需要特别注意两者的协调问题。通常的做法是让Qt管理主事件循环,而SDL2仅用于特定的多媒体任务,如视频渲染或音频播放。可以通过多线程方式运行SDL2的渲染逻辑,以避免阻塞Qt主线程。 ### 示例代码:SDL2视频播放功能 以下是一个简单的示例,展示如何在Qt中使用SDL2播放YUV格式的视频帧: ```cpp void SDLWidget::renderFrame(unsigned char* yuvData, int width, int height) { SDL_UpdateYUVTexture(sdlTexture, NULL, yuvData, width, yuvData + width * height, width / 2, yuvData + width * height * 5 / 4, width / 2); SDL_RenderClear(sdlRenderer); SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); SDL_RenderPresent(sdlRenderer); } ``` 此函数接收YUV数据并将其更新到纹理中,然后在渲染器上绘制。 ### 注意事项 - **跨平台兼容性**:确保在不同平台上都正确配置了SDL2的路径库文件。 - **性能优化**:对于高分辨率或高帧率的视频,考虑使用硬件加速(如OpenGL)来提升性能。 - **内存管理**:及时释放SDL2分配的资源,如窗口、渲染器纹理对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值