Пример использования байдингов OpenGL для языка daScript
Попробовал разобраться с использованием байндингов библиотек к языку daScript. В качестве тестовой задачки решил портировать на daScript + OpenGL эту демку. Программа реализует алгоритм клиппинга модели несколькими плоскостями, с заполненнием отрезанных частей “крышками”, чтобы не было видно внутренней поверхности модели (скрин).
Алгоритм:
Отрисовать модель с шейдером отсечения (с “дырками”)
Заполнить буфер трафарета значениями так, чтобы пометить пиксели, которые нужно закрыть: — Отрисовать внутреннюю сторону модели, увеличивая значение в буфере трафарета — Отрисовать внешнюю сторону модели, уменьшая значение в буфере трафарета — Заполнять буфер трафарета в только в точках, в которых плоскость отсечения повернута к камере (для корректной работы нескольких плоскостей отсечения одновременно)
Отрисовать плоскости отсечения по полученной маске (получаются закрывающие “крышки”)
Для создания байндингов библиотек к daScript используется dasClangBind, с помощью которого сделаны обёртки для нескольких библиотек, включая OpenGL. Последний из примеров демонстрирует загрузку и отображение модели из obj-файла. Этот пример можно взять за основу. Для задания настроек отсекающих плоскостей можно взять байндинг к imgui.
Эти модули также тащат за собой glfw для создания окна и stbImage. Шаблон C++ кода для подключения модулей:
Пустое окно на glfw + imgui можно создать так:
не забыли создать и очистить буфер трафарета (GL_STENCIL_BUFFER_BIT)
block в daScript — безымянная функция, которая захватывает переменные по ссылке (более быстрая, чем лямбда-функции, которые могут управлять способом захвата)
defer — макрос для добавления выражений в блок finally
Исходная демка использует библиотеки three.js и ColladaLoader.js для загрузки меша из dae файла, но можно конвертировать dae в obj, чтобы использовать код загрузки меша из примера daScript. Загрузка меша:
Интересная штука — DSL для работы с шейдерами (glsl_internal, набор макросов для того, чтобы писать шейдеры как обычные функции в daScript, а также работать с uniform переменными почти как с обычными переменными языка. Пример передачи uniform-ов в шейдер:
Помимо простой привязки функций библиотеки, сгенерированной с помощью dasClangBind, написаны также макросы для “daScript-ивизации” кода. Вместо императивного вызова функции glUniformXXX, программист декларирует намерение “эта переменная - uniform для шейдера” — аннотация uniform.
За счёт этого скриптовый язык становится не "условным бейсиком" для императивного вызова функций, а способом приблизить библиотеку к предметной области, в терминах которой мыслит и работает программист
Исходная демка на three.js использует для описания состояния рендера концепцию материалов этой библиотеки, но несложно сопоставить свойства материлов с параметрами OpenGL
Вывод отсекающих граней:
Как отмечено в описании алгоритма референсной демки, такой подход нормально работает с одной плоскостью отсечения, но с нескольими плоскостями даёт неверный результат (повёрнутые “от камеры” плоскости отсечения также вносят вклад в маску и портят результат в буфере трафарета — выводят лишние “дырки” или “крышки” в таких местах:
Коррекция буфера трафарета для граней, повернутых от камеры#
На этом этапе обнаруживаются отличия между демкой-референсом и примером из daScript. Позиция камеры в референсной демке попадает в шейдера “автоматически”, эта переменная устанавливается библиотекой three.js. Для примера на daScript нужно передать её вручную и учесть то, что системы координат в демках различаются. Поворот в примере daScript задаётся через матрицу v_model, так что для трансформации камеры в систему координат модели и плоскостей отсечения нужно также “довернуть” её, умножив на матрицу модели.
Можно обратить внимание на идентичный синтаксис умножения вектора на матрицу в коде вершинного шейдера для трансфорфмации вершин меша, и обычном скрипте на daScript.
Теперь наконец отсечения смотрятся корректно под любым углом.
daScript хорош, чтобы поиграться с демками графических эффектов :)
подход авторов к написанию байндингов — автоматическая обёртка на c/c++-функциями + “daScript-тификация” кода — создание макросов, упрощающих работу с библиотекой
вообще, демку стоило бы ещё перевести на режим live-изменений, тянет на отдельный туториал
Ну и более глобальный вывод про совокупность всех фич языка — если большая часть кода на языке делает то, что сложно или долго делать на других языках, с какого-то момента разработки сама программа может получить какие-то свойства, которых нет у программ на других языках (потому что их было слишком долго или трудно реализовывать).