mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-12-03 16:47:26 +00:00
341 lines
10 KiB
Text
341 lines
10 KiB
Text
|
// Copyright 2021 Michael Fabian Dirks <info@xaymar.com>
|
||
|
//
|
||
|
// Redistribution and use in source and binary forms, with or without
|
||
|
// modification, are permitted provided that the following conditions are met:
|
||
|
//
|
||
|
// 1. Redistributions of source code must retain the above copyright notice,
|
||
|
// this list of conditions and the following disclaimer.
|
||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||
|
// this list of conditions and the following disclaimer in the documentation
|
||
|
// and/or other materials provided with the distribution.
|
||
|
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||
|
// may be used to endorse or promote products derived from this software
|
||
|
// without specific prior written permission.
|
||
|
//
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Configuration
|
||
|
//------------------------------------------------------------------------------
|
||
|
#define IS_TRANSITION
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Defines
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Variations of PI/TAU
|
||
|
#define PI 3.141592653
|
||
|
#define TAU 6.283185307
|
||
|
#define PIm2 6.283185307
|
||
|
#define PIb2 1.570796326
|
||
|
#define PIb4 0.785398163
|
||
|
|
||
|
// Phi/Φ = Golden Ratio
|
||
|
#define PHI 1.61803398874989484820459
|
||
|
|
||
|
// e (Eulers Constant)
|
||
|
#define EULERS_CONSTANT 2,7182818284590452353602874713527
|
||
|
|
||
|
// Degrees <-> Radians Conversion
|
||
|
#define TO_RAD(x) (x * 0.017453292)
|
||
|
#define TO_DEG(x) (x * 57.295779513)
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Uniforms
|
||
|
//------------------------------------------------------------------------------
|
||
|
uniform float4x4 ViewProj<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
uniform float4 Time<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
uniform float4 ViewSize<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
// Filter Support
|
||
|
#ifdef IS_FILTER
|
||
|
uniform texture2d InputA<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
#endif
|
||
|
|
||
|
// Transition Support
|
||
|
#ifdef IS_TRANSITION
|
||
|
uniform texture2d InputA<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
uniform texture2d InputB<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
uniform float TransitionTime<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
uniform int2 TransitionSize<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
#endif
|
||
|
|
||
|
uniform int RandomSeed<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
uniform float4x4 Random<
|
||
|
bool automatic = true;
|
||
|
>;
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Structures
|
||
|
//------------------------------------------------------------------------------
|
||
|
struct VertexInformation {
|
||
|
float4 position : POSITION;
|
||
|
float4 texcoord0 : TEXCOORD0;
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Samplers
|
||
|
//------------------------------------------------------------------------------
|
||
|
sampler_state PointRepeatSampler {
|
||
|
Filter = Point;
|
||
|
AddressU = Repeat;
|
||
|
AddressV = Repeat;
|
||
|
};
|
||
|
sampler_state LinearRepeatSampler {
|
||
|
Filter = Linear;
|
||
|
AddressU = Repeat;
|
||
|
AddressV = Repeat;
|
||
|
};
|
||
|
|
||
|
sampler_state PointMirrorSampler {
|
||
|
Filter = Point;
|
||
|
AddressU = Mirror;
|
||
|
AddressV = Mirror;
|
||
|
};
|
||
|
sampler_state LinearMirrorSampler {
|
||
|
Filter = Linear;
|
||
|
AddressU = Mirror;
|
||
|
AddressV = Mirror;
|
||
|
};
|
||
|
|
||
|
sampler_state PointClampSampler {
|
||
|
Filter = Point;
|
||
|
AddressU = Clamp;
|
||
|
AddressV = Clamp;
|
||
|
};
|
||
|
sampler_state LinearClampSampler {
|
||
|
Filter = Linear;
|
||
|
AddressU = Clamp;
|
||
|
AddressV = Clamp;
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Functions
|
||
|
//------------------------------------------------------------------------------
|
||
|
VertexInformation DefaultVertexShader(VertexInformation vtx) {
|
||
|
vtx.position = mul(float4(vtx.position.xyz, 1.0), ViewProj);
|
||
|
return vtx;
|
||
|
};
|
||
|
|
||
|
float2 rotate2d(float2 v, float angle) {
|
||
|
float s = sin(angle);
|
||
|
float c = cos(angle);
|
||
|
float2x2 m = float2x2(c, -s, s, c);
|
||
|
return mul(m, v);
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Options
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
uniform float2 _400_Offset<
|
||
|
string name = "Rotation Center";
|
||
|
string field_type = "slider";
|
||
|
string suffix = "%";
|
||
|
float2 minimum = {-50., -50.};
|
||
|
float2 maximum = {50., 50.};
|
||
|
float2 scale = {.01, .01};
|
||
|
float2 step = {.01, .01};
|
||
|
> = {0., 0.};
|
||
|
|
||
|
uniform float _500_Rotation<
|
||
|
string name = "Rotation";
|
||
|
string field_type = "slider";
|
||
|
string suffix = "°";
|
||
|
float minimum = -180.;
|
||
|
float maximum = 180.;
|
||
|
float scale = -1.;
|
||
|
float step = .01;
|
||
|
> = 90.;
|
||
|
|
||
|
uniform float _600_BlurRotation<
|
||
|
string name = "Blur Rotation";
|
||
|
string field_type = "slider";
|
||
|
string suffix = "°";
|
||
|
float minimum = -90.;
|
||
|
float maximum = 90.;
|
||
|
float scale = 1.;
|
||
|
float step = .01;
|
||
|
> = 30.;
|
||
|
|
||
|
uniform float _700_TransitionRange<
|
||
|
string name = "Transition Range";
|
||
|
string field_type = "slider";
|
||
|
string suffix = "%";
|
||
|
float minimum = 0.;
|
||
|
float maximum = 100.;
|
||
|
float scale = .01;
|
||
|
float step = .01;
|
||
|
> = 10.;
|
||
|
|
||
|
uniform int _900_MaximumBlurSamples<
|
||
|
string name = "Max. Blur Samples";
|
||
|
string description = "The maximum of samples to use for the Blur effect. Higher number looks nicer, but has way higher GPU usage.";
|
||
|
string field_type = "slider";
|
||
|
string suffix = "";
|
||
|
int minimum = 4;
|
||
|
int maximum = 128;
|
||
|
int scale = 1;
|
||
|
int step = 1;
|
||
|
> = 8;
|
||
|
|
||
|
uniform bool _990_MirrorInsteadOfRepeat<
|
||
|
string name = "Mirror instead of Repeat";
|
||
|
> = true;
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Effect
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Edge is 50% progress
|
||
|
// Rotates n° forward/backward, then switches to B, then rotates back to zero in the other direction.
|
||
|
// While rotated, blurs around the same axis.
|
||
|
// Starts slow, speeds up to 100%, then slows down again. Looks like sine curve.
|
||
|
|
||
|
float2 uv_to_xy(float2 uv) {
|
||
|
return ((uv - float2(.5, .5)) + _400_Offset) * TransitionSize.xy;
|
||
|
}
|
||
|
|
||
|
float2 xy_to_uv(float2 xy) {
|
||
|
return ((xy / TransitionSize.xy) - _400_Offset) + float2(.5, .5);
|
||
|
}
|
||
|
|
||
|
float4 SampleTexture(texture2d tex, float2 uv) {
|
||
|
if (_990_MirrorInsteadOfRepeat) {
|
||
|
return tex.Sample(LinearMirrorSampler, uv);
|
||
|
} else {
|
||
|
return tex.Sample(LinearRepeatSampler, uv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float4 sample_with_blur(texture2d tex, float2 xy, float max_angle, uint max_steps) {
|
||
|
float angle_step = max_angle / float(max_steps);
|
||
|
|
||
|
float4 final = SampleTexture(tex, xy_to_uv(xy));
|
||
|
for (uint step = 1; step <= max_steps; step++) {
|
||
|
float angle = angle_step * float(step);
|
||
|
float2 xy_p = rotate2d(xy, TO_RAD(angle));
|
||
|
float2 xy_n = rotate2d(xy, TO_RAD(-angle));
|
||
|
|
||
|
final += SampleTexture(tex, xy_to_uv(xy_p));
|
||
|
final += SampleTexture(tex, xy_to_uv(xy_n));
|
||
|
}
|
||
|
final /= (max_steps * 2u + 1u);
|
||
|
|
||
|
return final;
|
||
|
}
|
||
|
|
||
|
float4 DefaultPixelShader(VertexInformation vtx) : TARGET {
|
||
|
// Precalculate some important information.
|
||
|
float2 uv = vtx.texcoord0.xy;
|
||
|
// - UV offset towards the "center".
|
||
|
float2 uv_offset = ((uv - float2(.5, .5)) + _400_Offset);
|
||
|
float2 xy = uv_to_xy(uv);
|
||
|
// - Distance to the "center" from the offset coordinates.
|
||
|
float distance_to_center = distance(uv_offset, float2(.0, 0.));
|
||
|
// - Maximum Blur Angle
|
||
|
float max_blur_angle = _600_BlurRotation;
|
||
|
max_blur_angle *= sin(distance_to_center * PIb2);
|
||
|
max_blur_angle *= cos((TransitionTime * 2. + 1. ) * PI) * .5 + .5;
|
||
|
// - Maximum number of samples for blurring.
|
||
|
uint max_blur_samples = uint(_900_MaximumBlurSamples); // TODO: Calculate this value?
|
||
|
|
||
|
// Calculate the angle of the effect, this goes to _500_Rotation over 0-50%,
|
||
|
// then goes back in reverse in order to give the illusion of a continuous
|
||
|
// motion.
|
||
|
float angle_a = sin((TransitionTime * 2.) * PIb2 - PIb2) * _500_Rotation + _500_Rotation;
|
||
|
float angle_b = sin((TransitionTime * 2. - 1.) * PIb2) * _500_Rotation - _500_Rotation ;
|
||
|
|
||
|
// Generate the rotated XY position for sampling.
|
||
|
float2 xy_a = rotate2d(xy, TO_RAD(angle_a));
|
||
|
float2 xy_b = rotate2d(xy, TO_RAD(angle_b));
|
||
|
|
||
|
// Calculate the weight of A and B.
|
||
|
// - Convert the transition point to a range from -1 to 1.
|
||
|
float weight_a = (TransitionTime - .5) * 2.; // We don't actually want this range, but it is easier to work with.
|
||
|
// - Offset it by the transition range.
|
||
|
weight_a += _700_TransitionRange;
|
||
|
// - Divide it by the transition range times two.
|
||
|
weight_a /= _700_TransitionRange * 2.;
|
||
|
// - Clamp it back to a range from 0 to 1.
|
||
|
weight_a = 1. - (cos((clamp(weight_a, 0., 1.) + 1.) * PI) * .5 + .5); // Curve
|
||
|
//weight_a = 1. - clamp(weight_a, 0., 1.); // Alternative linear
|
||
|
// - The weight for the B side is the one-minus of the A side.
|
||
|
float weight_b = 1. - weight_a;
|
||
|
|
||
|
// Store some immediate information.
|
||
|
float4 color_a = float4(0., 0., 0., 0.);
|
||
|
float4 color_b = float4(0., 0., 0., 0.);
|
||
|
|
||
|
// Only sample A and B if they are needed.
|
||
|
if (weight_a >= 0.001) {
|
||
|
color_a = sample_with_blur(InputA, xy_a, max_blur_angle, max_blur_samples);
|
||
|
}
|
||
|
if (weight_b >= 0.001) {
|
||
|
color_b = sample_with_blur(InputB, xy_b, max_blur_angle, max_blur_samples);
|
||
|
}
|
||
|
|
||
|
// Return a blended color.
|
||
|
return lerp(color_a, color_b, weight_b);
|
||
|
};
|
||
|
|
||
|
technique Draw
|
||
|
{
|
||
|
pass
|
||
|
{
|
||
|
vertex_shader = DefaultVertexShader(vtx);
|
||
|
pixel_shader = DefaultPixelShader(vtx);
|
||
|
};
|
||
|
};
|
||
|
|
||
|
technique Version1_Normal
|
||
|
{
|
||
|
pass
|
||
|
{
|
||
|
vertex_shader = DefaultVertexShader(vtx);
|
||
|
pixel_shader = DefaultPixelShader(vtx);
|
||
|
};
|
||
|
};
|
||
|
|
||
|
|
||
|
technique Version1_Mirror
|
||
|
{
|
||
|
pass
|
||
|
{
|
||
|
vertex_shader = DefaultVertexShader(vtx);
|
||
|
pixel_shader = DefaultPixelShader(vtx);
|
||
|
};
|
||
|
};
|