bgfx - обёртка над низкоуровневыми графическими api (все основные под desktop, телефоны и web).
dasBGFX - байндинги библиотеки к daScript.
bgfx
умеет использовать glfw
или sdl
для создания окна, dasBGFX
использует dasGlfw
. Также модуль содержит DSL для трансляции шейдеров с daScript-like языка на BGFX (glsl-like)
В качестве инструкции по сборке можно использовать заметки:
daScript - live-режим - сборка с glfw
daScript. SFML, dasBox и semi-transparent render target - сборка байндингов для sfml
По аналогии:
- выкачиваем сабмодули из git
- включаем дефайны в cmake
- добавляем зависимости от модулей dasGlfw и dasBGFX в какой-нибудь из тестовых проектов
- перегенерируем решение
- проверяем, что настройках проекта добавились пути к include и lib от модулей
- запускаем простые примеры
Независимая от порядка рендера полупрозрачность#
Попробуем портировать чуть более сложный пример - Weighted Blended Order-Independent Transparency
с bgfx
(иначе вообще зачем он нужен?)
Очень кратко об идее:
“Традиционный” рендер полупрозрачных полигонов чувствителен и порядку рендера, для корректного смешивания необходимо выводить объекты от дальнего к ближнему по отношению к наблюдателю. Это не всегда удобно, так как:
- объекты могут быть “сплетены”, из-за чего их невозможно отсортировать
- трата времени на сортировку
непрозрачные объекты могут при определенных условиях требовать рендеринга как полупрозрачные - забор с дырками на большом отдалении может содержать прозрачный и непрозрачный полигон, и начать “просвечивать”
Способы избавиться от необходимости сортировки:
- порезать объекты на части (требует сложной подготовки)
- хранить не только цвет/альфу ближайшего к наблюдателю пикселя, а список из нескольких (требует расхода памяти)
- переопределить оператор смешивания на коммутативный
К третьей группе относится метод Weighted Blended Order-Independent Transparency
пост от автора, дополнение и пейпер. Также - [движок] с реализацией, и описание в книге Computer graphics. Principles and practice
(всё - от него же).
Пара других примеров - имплементация в движке cesium (webgl) и пример на bgfx
Собственно, спортить пример напрямую не получилось, поэтому реализация скорее не перевод примера, а из поста, “в лоб”, возможно некорректная, но вроде похожая не референсный скриншот из оригинала.
Других прямых референсов откопать не удалось, а сравнить с “традиционной” прозрачность нельзя, так как независимые от порядка методы содержат погрешности, конкретно weighted blended
, как следует из названия, содержит “веса” объектов, в зависимости от их прозрачности и удаленности от камеры, так что для корректного использования необходимо представлять себе диапазоны возможных значений. Так что тут скорее “проба API
“, а не нормальная реализация.
Инициализация#
После создания окна
Создание двух view
Один - для рендера в 2 текстуры, второй - для смешивания текстур с восстановлением альфы на экранеvar display_w, display_h : int
glfwGetWindowSize(window, safe_addr(display_w), safe_addr(display_h))
bgfx_reset(uint(display_w), uint(display_h), BGFX_RESET_VSYNC, bgfx_texture_format COUNT)
bgfx_set_view_rect(0u, 0u, 0u, uint(display_w), uint(display_h))
bgfx_set_view_rect(1u, 0u, 0u, uint(display_w), uint(display_h))
Создание 2х RT-текстур, в сумме 5 каналов 16-битных float-каналов - RGBA + модификатор
var fb_textures = [[auto
bgfx_create_texture_2d(1280u, 720u, false, 1u, bgfx_texture_format RGBA16F, BGFX_TEXTURE_RT, null);
bgfx_create_texture_2d(1280u, 720u, false, 1u, bgfx_texture_format R16F, BGFX_TEXTURE_RT, null)
]]
//создаём фреймбуффер, к которому прикрепляем обе текстуры, и привязываем его к первому view
var fbh = bgfx_create_frame_buffer_from_handles(2u, safe_addr(fb_textures),true)
bgfx_set_view_frame_buffer(0u, fbh)
Заливка текстур начальным значением
let pal0 = [[float 0.0; 0.0; 0.0; 0.0]] //для RGBA каналов
let pal1 = [[float 1.0; 1.0; 1.0; 1.0]] //для канала с весами
bgfx_set_view_frame_buffer(0u, fbh)
bgfx_set_palette_color(0u, safe_addr(pal0))
bgfx_set_palette_color(1u, safe_addr(pal1))
bgfx_set_view_clear_mrt(0u , BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH, 1.0f, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u)
Проход 1 - рендер полупрозрачных объектов#
(алгоритм позволяет использовать уже накопленный буфер глубины и совмещается с другими техниками рендера, но тут - наиболее простая референсная сцена с тремя полупрозрачными квадами)
Шейдер
var [[in]] a_position : float3
var [[in]] a_color0 : float4
var [[inout, semantics=POSITION1]] v_pos : float4
var [[inout]] v_color0 : float4
var [[uniform]] v_ortho : float4x4
[bgfx_vertex_buffer]
struct Vertex
position : float3
color0 : float4
[bgfx_vertex_program]
def vs_main
v_color0 = a_color0
gl_Position = v_ortho * float4(a_position, 1.0)
v_pos = gl_Position
[bgfx_fragment_program]
def fs_main
var color = v_color0
//не важно для 2d референсной сцены
let depth = v_pos.z/v_pos.w
//не забыть alpha-premultiply
color.xyz *= color.w
//настройка веса
let weight = color.w * clamp(0.03 / (1e-5 + pow(depth, 5.0) ), 0.01, 3000.0)
//запись в 1-ю rt текстуру RGBA, умноженного на вес
gl_FragData[0] = color * weight
//отдельное сохранение веса
gl_FragData[1] = color.wwww
Небольшое необходимое дополнение к DSL-шейдеров://объявление переменной для записи нескольких output-цветов (в несколько RT-текстур) в пиксельном шейдере
var gl_FragData = [[float4[4] float4(); float4(); float4(); float4()]]
Настройка смешивания и отрисовка
vs_main_bind_uniform()
fs_main_bind_uniform()
var stateNoDepth = ( uint64(0)
| BGFX_STATE_CULL_CW
| BGFX_STATE_WRITE_RGB
| BGFX_STATE_WRITE_A
| BGFX_STATE_DEPTH_TEST_ALWAYS
| BGFX_STATE_MSAA
)
//флажки для задания режима прозрачности (ONE, ONE) для первой RT1 и (ZERO, SRC_COLOR) для RT2
//separate blendState for RT2
let rt2_blendState =uint(
BGFX_STATE_BLEND_ZERO >> uint64(BGFX_STATE_BLEND_SHIFT) |
BGFX_STATE_BLEND_SRC_COLOR >> uint64(BGFX_STATE_BLEND_SHIFT) << uint64(4)
)
bgfx_set_state(
stateNoDepth | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE) | BGFX_STATE_BLEND_INDEPENDENT,
rt2_blendState
)
bgfx_submit(0u, program, 1u, BGFX_DISCARD_NONE)
В пейпере автора рассматривается этот подход и альтернативный, для gpu без поддержки задания раздельных режимов смешивания для различных render target. Они немного отличаются в коде шейдера и смешивании, важно не перепутать сочетание
Задание раздельных функций смешивания для render-target выглядят в BGFX страшно - в первый параметр передаётся флаг BGFX_STATE_BLEND_INDEPENDENT
, а во второй (который 32-битный и называется _rgba) - битовая маска для описания режима смешивания второй RT. В c-api это спрятано за макросом для немного менее страшной записи.
На этой стадии можно попробовать отрисовать сцену и подключиться к приложению через графический отладчик, чтобы проверить состояние RT-текстур (например, RenderDoc
):
текстура развёрнута по y
Проход 2 - восстановление альфа канала по текстуре с весами#
Шейдер
[bgfx_vertex_buffer]
struct VertexTex
position : float3
color0 : float4
[[type=FLOAT, size=2, semantics=TEXCOORD0, normalized=false, as_int=false]] uv : float2
var [[uniform, stage=0]] s_texColor0 : sampler2D
var [[uniform, stage=1]] s_texColor1 : sampler2D
[bgfx_vertex_program]
def vs_quad
v_texcoord0 = a_texcoord0
gl_Position = v_ortho * float4(a_position, 1.0)
[bgfx_fragment_program]
def fs_quad
let accum = texture2D(s_texColor0, v_texcoord0)
let opacity = texture2D(s_texColor1, v_texcoord0).x
//восстановление и ограничение диапазона
gl_FragColor = float4(accum.xyz / clamp(accum.w, 1e-4, 5e4), opacity)
Настройка смешивания и отрисовка
//привязка текстур к сэмплерам
s_texColor0 := fb_textures[0]
s_texColor1 := fb_textures[1]
//цвет фона и настройка смешивания
bgfx_set_view_clear(1u, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, RGBA_TO_BGFX_UCOLOR(0.75, 0.75, 0.75, 1.0), 1.0f, 0u)
bgfx_set_state(BGFX_STATE_WRITE_RGB | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_INV_SRC_ALPHA, BGFX_STATE_BLEND_SRC_ALPHA), 0u)
//вывод fullscreen квада
vs_quad_bind_uniform()
fs_quad_bind_uniform()
bgfx_submit(1u, program_quad, 1u, BGFX_DISCARD_NONE)
Результат