gfx/shader: Add Per-Instance/Activation/Frame Random values

This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2020-07-05 23:00:09 +02:00 committed by Michael Fabian Dirks
parent 3de7a87de6
commit 717b38f3f7
8 changed files with 111 additions and 30 deletions

View file

@ -93,6 +93,8 @@ Shader.Shader.Size="Size"
Shader.Shader.Size.Description="Size of the source, can be one of the following units:\n- '###.##%' which is relative to the parent source, filter or scene.\n- '###' to specify the size in pixels. (Default)\nIf the value can't be parsed, it will be treated as 100%." Shader.Shader.Size.Description="Size of the source, can be one of the following units:\n- '###.##%' which is relative to the parent source, filter or scene.\n- '###' to specify the size in pixels. (Default)\nIf the value can't be parsed, it will be treated as 100%."
Shader.Shader.Size.Width="Width" Shader.Shader.Size.Width="Width"
Shader.Shader.Size.Height="Height" Shader.Shader.Size.Height="Height"
Shader.Shader.Seed="Randomization Seed"
Shader.Shader.Seed.Description="Seed used for the Per-Instance, Per-Activation and Per-Frame random values.\nThe same seed will always produce identical results if the identical number of runs were made."
Shader.Parameters="Shader Parameters" Shader.Parameters="Shader Parameters"
Shader.Parameters.Description="All the shader parameters that the loaded shader offers.\nMake sure to refresh these every now and then." Shader.Parameters.Description="All the shader parameters that the loaded shader offers.\nMake sure to refresh these every now and then."
Filter.Shader="Shader" Filter.Shader="Shader"

View file

@ -133,12 +133,23 @@ void shader_instance::video_render(gs_effect_t* effect)
} }
} }
void streamfx::filter::shader::shader_instance::activate()
{
_fx->set_active(true);
}
void streamfx::filter::shader::shader_instance::deactivate()
{
_fx->set_active(false);
}
shader_factory::shader_factory() shader_factory::shader_factory()
{ {
_info.id = PREFIX "filter-shader"; _info.id = PREFIX "filter-shader";
_info.type = OBS_SOURCE_TYPE_FILTER; _info.type = OBS_SOURCE_TYPE_FILTER;
_info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW;
set_activity_tracking_enabled(true);
finish_setup(); finish_setup();
register_proxy("obs-stream-effects-filter-shader"); register_proxy("obs-stream-effects-filter-shader");
} }

View file

@ -43,6 +43,9 @@ namespace streamfx::filter::shader {
virtual void video_tick(float_t sec_since_last) override; virtual void video_tick(float_t sec_since_last) override;
virtual void video_render(gs_effect_t* effect) override; virtual void video_render(gs_effect_t* effect) override;
void activate() override;
void deactivate() override;
}; };
class shader_factory : public obs::source_factory<filter::shader::shader_factory, filter::shader::shader_instance> { class shader_factory : public obs::source_factory<filter::shader::shader_factory, filter::shader::shader_instance> {

View file

@ -30,20 +30,26 @@
#define ST_SHADER_SIZE ST_SHADER ".Size" #define ST_SHADER_SIZE ST_SHADER ".Size"
#define ST_SHADER_SIZE_WIDTH ST_SHADER_SIZE ".Width" #define ST_SHADER_SIZE_WIDTH ST_SHADER_SIZE ".Width"
#define ST_SHADER_SIZE_HEIGHT ST_SHADER_SIZE ".Height" #define ST_SHADER_SIZE_HEIGHT ST_SHADER_SIZE ".Height"
#define ST_SHADER_SEED ST_SHADER ".Seed"
#define ST_PARAMETERS ST ".Parameters" #define ST_PARAMETERS ST ".Parameters"
gfx::shader::shader::shader(obs_source_t* self, shader_mode mode) gfx::shader::shader::shader(obs_source_t* self, shader_mode mode)
: _self(self), _mode(mode), _base_width(1), _base_height(1), : _self(self), _mode(mode), _base_width(1), _base_height(1), _active(true),
_shader(), _shader_file(), _shader_tech("Draw"), _shader_file_mt(), _shader_file_sz(), _shader_file_tick(0), _shader(), _shader_file(), _shader_tech("Draw"), _shader_file_mt(), _shader_file_sz(), _shader_file_tick(0),
_width_type(size_type::Percent), _width_value(1.0), _height_type(size_type::Percent), _height_value(1.0), _width_type(size_type::Percent), _width_value(1.0), _height_type(size_type::Percent), _height_value(1.0),
_time(0), _time_loop(0), _loops(0), _random(), _have_current_params(false), _have_current_params(false), _time(0), _time_loop(0), _loops(0), _random(), _random_seed(0),
_rt_up_to_date(false), _rt(std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE)) _rt_up_to_date(false), _rt(std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE))
{ {
_random.seed(static_cast<unsigned long long>(time(NULL))); // Intialize random values.
_random.seed(static_cast<unsigned long long>(_random_seed));
for (size_t idx = 0; idx < 16; idx++) {
_random_values[idx] =
static_cast<float_t>(static_cast<double_t>(_random()) / static_cast<double_t>(_random.max()));
}
} }
gfx::shader::shader::~shader() {} gfx::shader::shader::~shader() {}
@ -180,21 +186,13 @@ void gfx::shader::shader::defaults(obs_data_t* data)
obs_data_set_default_string(data, ST_SHADER_TECHNIQUE, ""); obs_data_set_default_string(data, ST_SHADER_TECHNIQUE, "");
obs_data_set_default_string(data, ST_SHADER_SIZE_WIDTH, "100.0 %"); obs_data_set_default_string(data, ST_SHADER_SIZE_WIDTH, "100.0 %");
obs_data_set_default_string(data, ST_SHADER_SIZE_HEIGHT, "100.0 %"); obs_data_set_default_string(data, ST_SHADER_SIZE_HEIGHT, "100.0 %");
obs_data_set_default_int(data, ST_SHADER_SEED, static_cast<long long>(time(NULL)));
} }
void gfx::shader::shader::properties(obs_properties_t* pr) void gfx::shader::shader::properties(obs_properties_t* pr)
{ {
_have_current_params = false; _have_current_params = false;
{
obs_properties_add_button2(
pr, ST_REFRESH, D_TRANSLATE(ST_REFRESH),
[](obs_properties_t* props, obs_property_t* prop, void* priv) {
return reinterpret_cast<gfx::shader::shader*>(priv)->on_refresh_properties(props, prop);
},
this);
}
{ {
auto grp = obs_properties_create(); auto grp = obs_properties_create();
obs_properties_add_group(pr, ST_SHADER, D_TRANSLATE(ST_SHADER), OBS_GROUP_NORMAL, grp); obs_properties_add_group(pr, ST_SHADER, D_TRANSLATE(ST_SHADER), OBS_GROUP_NORMAL, grp);
@ -211,23 +209,20 @@ void gfx::shader::shader::properties(obs_properties_t* pr)
auto p = obs_properties_add_path(grp, ST_SHADER_FILE, D_TRANSLATE(ST_SHADER_FILE), OBS_PATH_FILE, "*.*", auto p = obs_properties_add_path(grp, ST_SHADER_FILE, D_TRANSLATE(ST_SHADER_FILE), OBS_PATH_FILE, "*.*",
path.c_str()); path.c_str());
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_FILE))); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_FILE)));
/*obs_property_set_modified_callback2(
p,
[](void* priv, obs_properties_t* props, obs_property_t* prop, obs_data_t* data) noexcept {
return reinterpret_cast<gfx::shader::shader*>(priv)->on_shader_or_technique_modified(props, prop, data);
},
this);*/
} }
{ {
auto p = obs_properties_add_list(grp, ST_SHADER_TECHNIQUE, D_TRANSLATE(ST_SHADER_TECHNIQUE), auto p = obs_properties_add_list(grp, ST_SHADER_TECHNIQUE, D_TRANSLATE(ST_SHADER_TECHNIQUE),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_TECHNIQUE))); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_TECHNIQUE)));
/*obs_property_set_modified_callback2( }
p,
[](void* priv, obs_properties_t* props, obs_property_t* prop, obs_data_t* data) noexcept { {
return reinterpret_cast<gfx::shader::shader*>(priv)->on_shader_or_technique_modified(props, prop, data); obs_properties_add_button2(
grp, ST_REFRESH, D_TRANSLATE(ST_REFRESH),
[](obs_properties_t* props, obs_property_t* prop, void* priv) {
return reinterpret_cast<gfx::shader::shader*>(priv)->on_refresh_properties(props, prop);
}, },
this);*/ this);
} }
if (_mode != shader_mode::Transition) { if (_mode != shader_mode::Transition) {
@ -245,6 +240,12 @@ void gfx::shader::shader::properties(obs_properties_t* pr)
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_SIZE))); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_SIZE)));
} }
} }
{
auto p = obs_properties_add_int_slider(grp, ST_SHADER_SEED, D_TRANSLATE(ST_SHADER_SEED),
std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), 1);
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_SHADER_SEED)));
}
} }
{ {
auto grp = obs_properties_create(); auto grp = obs_properties_create();
@ -360,6 +361,15 @@ void gfx::shader::shader::update(obs_data_t* data)
_height_value = std::clamp(sz_y.second, 0.01, 8192.0); _height_value = std::clamp(sz_y.second, 0.01, 8192.0);
} }
if (int32_t seed = static_cast<int32_t>(obs_data_get_int(data, ST_SHADER_SEED)); _random_seed != seed) {
_random_seed = seed;
_random.seed(static_cast<unsigned long long>(_random_seed));
for (size_t idx = 0; idx < 16; idx++) {
_random_values[idx] =
static_cast<float_t>(static_cast<double_t>(_random()) / static_cast<double_t>(_random.max()));
}
}
for (auto kv : _shader_params) { for (auto kv : _shader_params) {
kv.second->update(data); kv.second->update(data);
} }
@ -444,6 +454,12 @@ bool gfx::shader::shader::tick(float_t time)
_loops = -_loops; _loops = -_loops;
} }
// Recreate Per-Activation-Random values.
for (size_t idx = 0; idx < 8; idx++) {
_random_values[8 + idx] =
static_cast<float_t>(static_cast<double_t>(_random()) / static_cast<double_t>(_random.max()));
}
return false; return false;
} }
@ -456,17 +472,16 @@ void gfx::shader::shader::prepare_render()
kv.second->assign(); kv.second->assign();
} }
// Time: (Current Time), (Zero), (Zero), (Random Value) // float4 Time: (Current Time), (Zero), (Zero), (Random Value)
if (gs::effect_parameter el = _shader.get_parameter("Time"); el != nullptr) { if (gs::effect_parameter el = _shader.get_parameter("Time"); el != nullptr) {
if (el.get_type() == gs::effect_parameter::type::Float4) { if (el.get_type() == gs::effect_parameter::type::Float4) {
el.set_float4( el.set_float4(
_time, _time_loop, static_cast<float_t>(_loops), _time, _time_loop, static_cast<float_t>(_loops),
static_cast<float_t>(static_cast<double_t>(_random()) static_cast<float_t>(static_cast<double_t>(_random()) / static_cast<double_t>(_random.max())));
/ static_cast<double_t>(std::numeric_limits<unsigned long long>::max())));
} }
} }
// ViewSize: (Width), (Height), (1.0 / Width), (1.0 / Height) // float4 ViewSize: (Width), (Height), (1.0 / Width), (1.0 / Height)
if (auto el = _shader.get_parameter("ViewSize"); el != nullptr) { if (auto el = _shader.get_parameter("ViewSize"); el != nullptr) {
if (el.get_type() == gs::effect_parameter::type::Float4) { if (el.get_type() == gs::effect_parameter::type::Float4) {
el.set_float4(static_cast<float_t>(width()), static_cast<float_t>(height()), el.set_float4(static_cast<float_t>(width()), static_cast<float_t>(height()),
@ -474,6 +489,20 @@ void gfx::shader::shader::prepare_render()
} }
} }
// float4x4 Random: float4[Per-Instance Random], float4[Per-Activation Random], float4x2[Per-Frame Random]
if (auto el = _shader.get_parameter("Random"); el != nullptr) {
if (el.get_type() == gs::effect_parameter::type::Matrix) {
el.set_value(_random_values, 16);
}
}
// int32 RandomSeed: Seed used for random generation
if (auto el = _shader.get_parameter("RandomSeed"); el != nullptr) {
if (el.get_type() == gs::effect_parameter::type::Integer) {
el.set_int(_random_seed);
}
}
_rt_up_to_date = false; _rt_up_to_date = false;
} }
@ -576,3 +605,14 @@ void gfx::shader::shader::set_transition_size(std::uint32_t w, std::uint32_t h)
} }
} }
} }
void gfx::shader::shader::set_active(bool active)
{
_active = active;
// Recreate Per-Activation-Random values.
for (size_t idx = 0; idx < 4; idx++) {
_random_values[4 + idx] =
static_cast<float_t>(static_cast<double_t>(_random()) / static_cast<double_t>(_random.max()));
}
}

View file

@ -47,6 +47,7 @@ namespace gfx {
shader_mode _mode; shader_mode _mode;
std::uint32_t _base_width; std::uint32_t _base_width;
std::uint32_t _base_height; std::uint32_t _base_height;
bool _active;
// Shader // Shader
gs::effect _shader; gs::effect _shader;
@ -64,11 +65,13 @@ namespace gfx {
double_t _height_value; double_t _height_value;
// Cache // Cache
bool _have_current_params;
float_t _time; float_t _time;
float_t _time_loop; float_t _time_loop;
int32_t _loops; int32_t _loops;
std::mt19937_64 _random; std::mt19937_64 _random;
bool _have_current_params; int32_t _random_seed;
float_t _random_values[16]; // 0..4 Per-Instance-Random, 4..8 Per-Activation-Random 9..15 Per-Frame-Random
// Rendering // Rendering
bool _rt_up_to_date; bool _rt_up_to_date;
@ -121,6 +124,8 @@ namespace gfx {
void set_transition_time(float_t t); void set_transition_time(float_t t);
void set_transition_size(std::uint32_t w, std::uint32_t h); void set_transition_size(std::uint32_t w, std::uint32_t h);
void set_active(bool active);
}; };
} // namespace shader } // namespace shader
} // namespace gfx } // namespace gfx

View file

@ -88,12 +88,23 @@ void shader_instance::video_render(gs_effect_t* effect)
_fx->render(); _fx->render();
} }
void streamfx::source::shader::shader_instance::activate()
{
_fx->set_active(true);
}
void streamfx::source::shader::shader_instance::deactivate()
{
_fx->set_active(false);
}
shader_factory::shader_factory() shader_factory::shader_factory()
{ {
_info.id = PREFIX "source-shader"; _info.id = PREFIX "source-shader";
_info.type = OBS_SOURCE_TYPE_INPUT; _info.type = OBS_SOURCE_TYPE_INPUT;
_info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW;
set_activity_tracking_enabled(true);
finish_setup(); finish_setup();
register_proxy("obs-stream-effects-source-shader"); register_proxy("obs-stream-effects-source-shader");
} }

View file

@ -42,6 +42,9 @@ namespace streamfx::source::shader {
virtual void video_tick(float_t sec_since_last) override; virtual void video_tick(float_t sec_since_last) override;
virtual void video_render(gs_effect_t* effect) override; virtual void video_render(gs_effect_t* effect) override;
void activate() override;
void deactivate() override;
}; };
class shader_factory : public obs::source_factory<source::shader::shader_factory, source::shader::shader_instance> { class shader_factory : public obs::source_factory<source::shader::shader_factory, source::shader::shader_instance> {

View file

@ -110,9 +110,14 @@ bool shader_instance::audio_render(uint64_t* ts_out, obs_source_audio_mix* audio
[](void*, float_t t) { return t; }); [](void*, float_t t) { return t; });
} }
void shader_instance::transition_start() {} void shader_instance::transition_start() {
_fx->set_active(true);
}
void shader_instance::transition_stop() {} void shader_instance::transition_stop()
{
_fx->set_active(false);
}
shader_factory::shader_factory() shader_factory::shader_factory()
{ {
@ -120,6 +125,7 @@ shader_factory::shader_factory()
_info.type = OBS_SOURCE_TYPE_TRANSITION; _info.type = OBS_SOURCE_TYPE_TRANSITION;
_info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; _info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW;
//set_activity_tracking_enabled(true); // Handled via transition start/stop
finish_setup(); finish_setup();
register_proxy("obs-stream-effects-transition-shader"); register_proxy("obs-stream-effects-transition-shader");
} }