mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-15 08:15:06 +00:00
gfx/shader: Add Per-Instance/Activation/Frame Random values
This commit is contained in:
parent
3de7a87de6
commit
717b38f3f7
8 changed files with 111 additions and 30 deletions
|
@ -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.Width="Width"
|
||||
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.Description="All the shader parameters that the loaded shader offers.\nMake sure to refresh these every now and then."
|
||||
Filter.Shader="Shader"
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
_info.id = PREFIX "filter-shader";
|
||||
_info.type = OBS_SOURCE_TYPE_FILTER;
|
||||
_info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW;
|
||||
|
||||
set_activity_tracking_enabled(true);
|
||||
finish_setup();
|
||||
register_proxy("obs-stream-effects-filter-shader");
|
||||
}
|
||||
|
|
|
@ -43,6 +43,9 @@ namespace streamfx::filter::shader {
|
|||
|
||||
virtual void video_tick(float_t sec_since_last) 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> {
|
||||
|
|
|
@ -30,20 +30,26 @@
|
|||
#define ST_SHADER_SIZE ST_SHADER ".Size"
|
||||
#define ST_SHADER_SIZE_WIDTH ST_SHADER_SIZE ".Width"
|
||||
#define ST_SHADER_SIZE_HEIGHT ST_SHADER_SIZE ".Height"
|
||||
#define ST_SHADER_SEED ST_SHADER ".Seed"
|
||||
#define ST_PARAMETERS ST ".Parameters"
|
||||
|
||||
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),
|
||||
|
||||
_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))
|
||||
{
|
||||
_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() {}
|
||||
|
@ -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_SIZE_WIDTH, "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)
|
||||
{
|
||||
_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();
|
||||
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, "*.*",
|
||||
path.c_str());
|
||||
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),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
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) {
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
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();
|
||||
|
@ -360,6 +361,15 @@ void gfx::shader::shader::update(obs_data_t* data)
|
|||
_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) {
|
||||
kv.second->update(data);
|
||||
}
|
||||
|
@ -444,6 +454,12 @@ bool gfx::shader::shader::tick(float_t time)
|
|||
_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;
|
||||
}
|
||||
|
||||
|
@ -456,17 +472,16 @@ void gfx::shader::shader::prepare_render()
|
|||
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 (el.get_type() == gs::effect_parameter::type::Float4) {
|
||||
el.set_float4(
|
||||
_time, _time_loop, static_cast<float_t>(_loops),
|
||||
static_cast<float_t>(static_cast<double_t>(_random())
|
||||
/ static_cast<double_t>(std::numeric_limits<unsigned long long>::max())));
|
||||
static_cast<float_t>(static_cast<double_t>(_random()) / static_cast<double_t>(_random.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 (el.get_type() == gs::effect_parameter::type::Float4) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace gfx {
|
|||
shader_mode _mode;
|
||||
std::uint32_t _base_width;
|
||||
std::uint32_t _base_height;
|
||||
bool _active;
|
||||
|
||||
// Shader
|
||||
gs::effect _shader;
|
||||
|
@ -64,11 +65,13 @@ namespace gfx {
|
|||
double_t _height_value;
|
||||
|
||||
// Cache
|
||||
bool _have_current_params;
|
||||
float_t _time;
|
||||
float_t _time_loop;
|
||||
int32_t _loops;
|
||||
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
|
||||
bool _rt_up_to_date;
|
||||
|
@ -121,6 +124,8 @@ namespace gfx {
|
|||
void set_transition_time(float_t t);
|
||||
|
||||
void set_transition_size(std::uint32_t w, std::uint32_t h);
|
||||
|
||||
void set_active(bool active);
|
||||
};
|
||||
} // namespace shader
|
||||
} // namespace gfx
|
||||
|
|
|
@ -88,12 +88,23 @@ void shader_instance::video_render(gs_effect_t* effect)
|
|||
_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()
|
||||
{
|
||||
_info.id = PREFIX "source-shader";
|
||||
_info.type = OBS_SOURCE_TYPE_INPUT;
|
||||
_info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW;
|
||||
|
||||
set_activity_tracking_enabled(true);
|
||||
finish_setup();
|
||||
register_proxy("obs-stream-effects-source-shader");
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@ namespace streamfx::source::shader {
|
|||
|
||||
virtual void video_tick(float_t sec_since_last) 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> {
|
||||
|
|
|
@ -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 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()
|
||||
{
|
||||
|
@ -120,6 +125,7 @@ shader_factory::shader_factory()
|
|||
_info.type = OBS_SOURCE_TYPE_TRANSITION;
|
||||
_info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW;
|
||||
|
||||
//set_activity_tracking_enabled(true); // Handled via transition start/stop
|
||||
finish_setup();
|
||||
register_proxy("obs-stream-effects-transition-shader");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue