Thursday 3 March 2011

Программирование под Linux: SDL и OpenGL

По просьбе на форуме решил написать простенькую статью о программировании графики под linux. Собственно, оснавная часть графического (openGL) кода в разных ОС остаётся практически без изменений. Различаются только способы инициализации программы (точнее, окна). В windows собственно есть только winAPI и различные обёртки под него. В linux же есть две основные оконные библиотеки: gtk и qt4. Если вам интересно, вы можете найти в интернете способы инициализации окна под них. Я же опишу более простой способ: библиотека SDL.

SDL - это кроссплатформенная библиотека, предоставляющая доступ к видео, аудио, сетевому и другому оборудованию.
Нам SDL понадобится для простого создания окна, чтобы нам не пришлось заботится об установленных библиотеках:
мы скажем SDL "создай нам окошко", и оно создаст, независимо от того, какая графическая оболочка активна у пользователя:
QT или gtk. Мало того, установив эту библиотеку под windows, вы сможете практически без изменения использовать один и тот же
код инициализации в обоих системах.


Где её взять?
http://www.libsdl.org/ - официальный сайт проекта, где можно найти всё необходимое.
Но я использую Gentoo Linux, поэтому мне достаточно набрать "emerge libsdl", и библиотека будет установлена. В большинстве других дистрибутивов есть свои менеджеры пакетов, также позволяющие установить что-либо довольно просто.

Начинаем программировать.
Создадим один файлик, main.cpp.
Заметьте, кода для простой инициализации здесь гораздо меньше, чем в windows.

#include <SDL.h>        // Хидер библиотеки SDL
#include <GL/gl.h>      // Хидеры OpenGL & glu
#include <GL/glu.h>

// Сначала напишем простую функцию, которая будет отрисовывать
// цветной треугольник
void Draw()
{
    // Перемещаем камеру назад
    glTranslatef(0,0,-5);
    // Рисуем треугольник
    glBegin(GL_TRIANGLES);
        glColor3f(0,1,1);
        glVertex3f(-1,0,0);
        glColor3f(1,0,1);
        glVertex3f(1,-1,0);
        glColor3f(1,1,0);
        glVertex3f(1,0,0);
    glEnd();
}

int main()
{
    // Инициализируем SDL. В качестве параметра передаём, какие именно
    // подсистемы нужно инициализировать. Например, можно указать SDL_INIT_AUDIO
    // или SDL_INIT_JOYSTICK для доступа к звуку и джойстику соответственно.
    // Ниже я приведу список возможных флагов.

    // Если произошла ошибка, SDL_Init вернёт "-1"
    if(SDL_Init(SDL_INIT_VIDEO) < 0){
        // Выводим ошибку
        printf("Error while initializing SDL: %s\n",SDL_GetError());
        // и выходим из программы
        exit(1);
    }

    // устанавливаем аттрибуты OpenGL. Их больше, чем здесь, но пока
    // нам нужно только эти:
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);    // использовать двойной буфер
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);    // размер в битах цветовых составляющих буфера
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);  // *---
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);   // *---

    // Далее создаём окно, указывая его размер, глубину цвета, и флаги.
    // Возможные флаги я также перечислю ниже. Для указания нескольких флагов сразу
    // перечисляйте их как 'SDL_FULLSCREEN | SDL_OPENGL | ......'
    if(SDL_SetVideoMode(640,480,32,SDL_OPENGL) == NULL)
    {
        printf("Error while setting up video mode: %s\n", SDL_GetError());
        exit(1);
    }

    // Ну а дальше оперируем стандартными функциями OpenGL, которые вам должны
    // быть знакомы:
    glClearColor(0,0,0,0);
    glClearDepth(1.0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, 640/480,0.1f,100.0f);
    glMatrixMode(GL_MODELVIEW);

    // Небольшой цикл программы. Ничего не делает, ждёт нажатия
    // escape для завершения.
    bool done=false;
    SDL_Event event;
    while(!done)
    {
        while(SDL_PollEvent(&event)){
            if(event.type == SDL_QUIT)
                done=true;
            if(event.type == SDL_KEYDOWN)
            {
                if(event.key.keysym.sym == SDLK_ESCAPE)
                    done=true;
            }
        }
    Draw();
    glFlush();
    SDL_GL_SwapBuffers();
    }

    // Завершаем работу SDL,
    SDL_Quit();

    return 0;
}

Теперь нужно откомпилировать написанное. Возможно, это будет немного сложновато, если вы раньше не программировали под *nix.
Войдите в шелл (терминал, xterm/console), перейдите в директорию, где лежит файл main.cpp
Строка для компиляции:

g++ main.cpp -lGL -lGLU -lSDL -I/usr/include/SDL -o prog

Разберём по пунктам.
g++ - Команда компилятору
main.cpp - только что написанный файл
-lGL - линковка библиотеки GL
-lGLU - линковка библиотеки GLU
-lSDL - линковка библиотеки SDL
-I/usr/include/SDL - указываем путь к инклудам SDL. Он может различатся в разных системах,
в таком случае вам нужно найти его самостоятельно.
-o prog - Указываем, как должен называтся обьект на выходе.

После выполнения этой команды, если не произошло ошибок, в этой же директории должен появится бинарный файл "prog". Запустите его, набрав "./prog", и любуйтесь своей программой.

Возможные флаги SDL_Init():
SDL_INIT_TIMER
SDL_INIT_AUDIO
SDL_INIT_VIDEO
SDL_INIT_CDROM
SDL_INIT_JOYSTICK
SDL_INIT_EVERYTHING
SDL_INIT_NOPARACHUTE
SDL_INIT_EVENTTHREAD


Возможные флаги SDL_SetVideoMode():
SDL_SWSURFACE - создает видео поверхность в оперативной памяти.
SDL_HWSURFACE - создает видео поверхность в памяти видеокарты.
SDL_ASYNCBLIT - использование асинхронного обновления экрана. Обычно это замедляет работу 
на обычных компьютерах, но значительно ускоряет на многопроцессорных системах.
SDL_ANYFORMAT - обчно, когда указанная глубина цвета невозможна на видеокарте, SDL эмулирует 
ее с помощью специальной теневой поверхности. Это замедляет работу программы. 
Указывая этот флаг, вы запрещаете библиотеке делать такие преобразования и 
застявляете использовать "неправильную" поверхность в любом случае.
SDL_HWPALETTE - предоставляет программе эксклюзивный доступ к палитре. Без этого флага вы 
не всегда сможете получить доступ к цветам функциями SDL_SetColors и SDL_SetPalette
SDL_DOUBLEBUF - включает двойной буфер. Это возможно только вместе с флагом SDL_HWSURFACE. 
Функция SDL_Flip перключает буферы, обновляя экран. То есть, когда вы рисуете 
что-либо, то все это отображается на невидимом буфере, а потом буферы переключаются 
и невидимый становится видимым. Когда двойной буфер недоступен, функция SDL_Flip 
вызывает функцию SDL_UpdataRects.
SDL_FULLSCREEN - SDL пытается переключиться в полноэкранный режим. Если по каким-либо причинам 
запрошеный размер экрана не может установиться, то будет выбран следующий больший 
режим, а изображение будет отцентровано на черном экране.
SDL_OPENGL - создает контекст OpenGL. Перед этим необходимо установить видео атрибуты 
OpenGL с помощью функции SDL_GL_SetAttribute.
SDL_OPENGLBLIT - также создает контекст OpenGL, но позволяет blitting-операции. Экранная 
поверхность (2D) может иметь альфа-канал (прозрачность) и должна обновляться 
только при помощи SDL_UpdateRects.
SDL_RESIZABLE - создает изменяемое окно. Когда пользователь изменяет размер окна, возникает 
событие SDL_VIDEORESIZE и можно опять вызвать функцию SDL_SetVideoMode, 
чтобы установить новые размеры окна.
SDL_NOFRAME - если возможно, создает окно без рамки и заголовка. Полноэкранный режим 
автоматически устанавливает этот флаг.

На этом пока все.

via masandilov.ru

4 comments:

  1. Спасибо, очень пригодится

    ReplyDelete
  2. Так и не осилил убунту и, судя по всему, напрасно.

    ReplyDelete
  3. Когда за пиратское ПО начнут действительно преследовать, то мы все бодренько перепрыгнем на какой нибудь дистрибутив линукс, напимер ту же убунту.

    ReplyDelete
  4. хороший пост

    + последователем:)

    ReplyDelete