Работа с OpenGL в Rust

OpenGL изначально написан на языке C, и активно используется в приложениях на этом языке и родственном ему C++. Однако, этим инструментом можно пользоваться в языке Rust, не теряя производительности, но приобретая дополнительные проверки безопасности. Ниже я рассмотрю один из способов делать это.

Для начала, следует сделать локальный крейт-библиотеку с биндингами для gl, добавив в build-dependencies крейт gl_generator. В нём необходимо настроить генерацию биндингов и билд-скрипте и создать структуру-обёртку для контекста gl.


В основной программе необходимо создать обёртки для текстур, шейдеров и программ, содержащих информацию о gl-контексте и id для работы с данными. Это необязательный шаг, можно работать с голыми данными из gl, как это обычно делают в C. Однако написание обёртки позволит в значительной степени инкапсулировать unsafe-код.


Для загрузки текстур можно использовать крейт image, позволяющий загрузить изображение из файла, а затем преобразовать его к потоку байтов в необходимом формате и загрузить его в видеопамять с использованием функций gl.TexImage2D и gl.BindTexture.


Также понадобятся абстракции для работы с vbo: структуры, которые будут передаваться с шейдером, помеченные атрибутом #[repr(C, packed)], который указывает компилятору, что структура должна размещаться в памяти, в соответствие со стандартом языка C, структуры для моделей, инкапсулирующие собственную загрузку в vbo.


Полезно также использовать крейт cgmath, так как он предоставляет большое количество функций для работы с матрицами, например матрицы смещения и поворота, а также матрицы преобразования для камеры.


Чтобы создать окно, к которому затем будет подключен gl, воспользуемся крейтом sdl2, являющимся обёрткой для одноимённой библиотеки. 


Создадим экземпляр окна:


let video_subsystem = sdl.video().expect(“Could not get sdl video subsystem”);

let window = video_subsystem

    .window(title, width, height)

    .opengl()

    .resizable()

    .build()

    .expect(“Could not create window”);


Затем получим экземпляр контекста gl:


let gl = gl::Gl::load_with(|s| {

    video_subsystem.gl_get_proc_address(s) as *const std::os::raw::c_void

});


Установим размеры видимого окна, очистим его каким-нибудь цветом и включим проверку глубины, для работы алгоритма z-buffer:


unsafe {

    gl.Viewport(0, 0, width as gl::types::GLint, height as gl::types::GLint);

    gl.ClearColor(red, green, blue, alpha);

    gl.Enable(gl::DEPTH_TEST);

}


Затем в цикле будем отрисовывать все необходимые модели, используя шейдеры и текстуры.


sdl позволяет получить обработчик событий клавиатуры и мыши. Из которого на каждой итерации цикла можно доставать все события и обрабатывать их. Таким образом можно даже написать простую игру. Для этого также будет полезно выключить отображение курсора и включить относительные координаты смещения мыши.