obs-StreamFX/data/effects/transform.effect
Michael Fabian 'Xaymar' Dirks 150b728419 filter/transform: Implement 'Corner Pin' mode
'Perspective' and 'Orthographic' work great if you know what the parameters were to generate the exact object position, but what if you don't know them? That is where 'Corner Pin' comes in! With it you can specify the exact location of every corner down to the micro-pixel, instead of fiddling with parameters.

Fixes #565
2023-04-05 18:51:29 +02:00

122 lines
2.9 KiB
Text

#include "shared.effect"
uniform texture2D InputA<
bool automatic = true;
>;
uniform float2 CornerTL<
string name = "Corner: Top Left";
string field_type = "slider";
string suffix = " %";
float2 minimum = {-100., -100.};
float2 maximum = {-200., -200.};
float2 step = {.01, .01};
float2 scale = {.01, .01};
> = {0., 0.};
uniform float2 CornerTR<
string name = "Corner: Top Right";
string field_type = "slider";
string suffix = " %";
float2 minimum = {-100., -100.};
float2 maximum = {-200., -200.};
float2 step = {.01, .01};
float2 scale = {.01, .01};
> = {100., 0.};
uniform float2 CornerBL<
string name = "Corner: Bottom Left";
string field_type = "slider";
string suffix = " %";
float2 minimum = {-100., -100.};
float2 maximum = {-200., -200.};
float2 step = {.01, .01};
float2 scale = {.01, .01};
> = {0., 100.};
uniform float2 CornerBR<
string name = "Corner: Bottom Right";
string field_type = "slider";
string suffix = " %";
float2 minimum = {-100., -100.};
float2 maximum = {-200., -200.};
float2 step = {.01, .01};
float2 scale = {.01, .01};
> = {100., 100.};
//------------------------------------------------------------------------------
// Technique: Corner Pin
//------------------------------------------------------------------------------
//
// Credits:
// - Inigo Quilez: https://www.iquilezles.org/www/articles/ibilinear/ibilinear.htm
//
// Parameters:
// - InputA: RGBA Texture
// - CornerTL: Corner "A"
// - CornerTR: Corner "B"
// - CornerBL: Corner "D"
// - CornerBR: Corner "C"
float2 cross2d(in float2 a, in float2 b) {
return (a.x * b.y) - (a.y * b.x);
};
float2 inverse_bilinear(in float2 p, in float2 a, in float2 b, in float2 c, in float2 d) {
float2 result = float2(-1., -1.);
float2 e = b - a;
float2 f = d - a;
float2 g = a-b+c-d;
float2 h = p-a;
float k2 = cross2d(g, f);
float k1 = cross2d(e, f) + cross2d(h, g);
float k0 = cross2d(h, e);
if (abs(k2) < .001) { // Edges are likely parallel, so this is a linear equation.
result = float2(
(h.x * k1 + f.x * k0) / (e.x * k1 - g.x * k0),
-k0 / k1
);
} else { // It's a quadratic equation.
float w = k1 * k1 - 4.0 * k0 * k2;
if (w < 0.0) { // Prevent GPUs from going insane.
return result;
}
w = sqrt(w);
float ik2 = 0.5/k2;
float v = (-k1 - w) * ik2;
float u = (h.x - f.x * v) / (e.x + g.x * v);
if (u < 0.0 || u > 1.0 || v < 0.0 || v > 1.0) {
v = (-k1 + w) * ik2;
u = (h.x - f.x * v) / (e.x + g.x * v);
}
result = float2(u, v);
}
return result;
};
float4 PSCornerPin(VertexData vtx) : TARGET {
// Convert from screen coords to potential Quad UV coordinates.
float2 uv = inverse_bilinear((vtx.uv * 2.) - 1., CornerTL, CornerTR, CornerBR, CornerBL);
if (max(abs(uv.x - .5), abs(uv.y - .5)) >= .5) {
return float4(0, 0, 0, 0);
}
return InputA.Sample(BlankSampler, uv);
};
technique CornerPin
{
pass
{
vertex_shader = DefaultVertexShader(vtx);
pixel_shader = PSCornerPin(vtx);
};
};